Bug 841666 - Use exponent over-estimation to truncate operations. r=h4writer
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Mon, 25 Feb 2013 15:41:06 -0800
changeset 134261 6a930768eb8298f33eb36ae953ff5dcc43c4dca3
parent 134260 018649ef8ce6b1404497be8c45311f9ef0f4b3be
child 134262 29de190ac31c1b28b6f41259b679dfcbd20a6e86
push id336
push userakeybl@mozilla.com
push dateMon, 17 Jun 2013 22:53:19 +0000
treeherdermozilla-release@574a39cdf657 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs841666
milestone22.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 841666 - Use exponent over-estimation to truncate operations. r=h4writer
js/src/ion/EdgeCaseAnalysis.cpp
js/src/ion/EdgeCaseAnalysis.h
js/src/ion/Ion.cpp
js/src/ion/IonAnalysis.cpp
js/src/ion/JSONSpewer.cpp
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/RangeAnalysis.cpp
js/src/ion/RangeAnalysis.h
js/src/jit-test/jit_test.py
js/src/jit-test/tests/ion/range-analysis.js
js/src/tests/lib/jittests.py
--- a/js/src/ion/EdgeCaseAnalysis.cpp
+++ b/js/src/ion/EdgeCaseAnalysis.cpp
@@ -41,48 +41,8 @@ EdgeCaseAnalysis::analyzeLate()
         if (mir->shouldCancel("Analyze Late (second loop)"))
             return false;
         for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++)
             riter->analyzeEdgeCasesBackward();
     }
 
     return true;
 }
-
-int
-EdgeCaseAnalysis::AllUsesTruncate(MInstruction *m)
-{
-    // If all uses truncate, the return value must be at least 1. If anything doesn't truncate
-    // 0 is explicitly returned.
-    int ret = 1;
-    for (MUseIterator use = m->usesBegin(); use != m->usesEnd(); use++) {
-        // See #809485 why this is allowed
-        if (use->consumer()->isResumePoint())
-            continue;
-
-        MDefinition *def = use->consumer()->toDefinition();
-        if (def->isTruncateToInt32())
-            continue;
-        if (def->isBitAnd())
-            continue;
-        if (def->isBitOr())
-            continue;
-        if (def->isBitXor())
-            continue;
-        if (def->isLsh())
-            continue;
-        if (def->isRsh())
-            continue;
-        if (def->isBitNot())
-            continue;
-        if (def->isAdd() && def->toAdd()->isTruncated()) {
-            ret = Max(ret, def->toAdd()->isTruncated() + 1);
-            continue;
-        }
-        if (def->isSub() && def->toSub()->isTruncated()) {
-            ret = Max(ret, def->toSub()->isTruncated() + 1);
-            continue;
-        }
-        // cannot use divide, since |truncate(int32(x/y) + int32(a/b)) != truncate(x/y+a/b)|
-        return 0;
-    }
-    return ret;
-}
--- a/js/src/ion/EdgeCaseAnalysis.h
+++ b/js/src/ion/EdgeCaseAnalysis.h
@@ -16,17 +16,16 @@ class MIRGraph;
 class EdgeCaseAnalysis
 {
     MIRGenerator *mir;
     MIRGraph &graph;
 
   public:
     EdgeCaseAnalysis(MIRGenerator *mir, MIRGraph &graph);
     bool analyzeLate();
-    static int AllUsesTruncate(MInstruction *m);
 };
 
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_edge_case_analysis_h__
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -933,16 +933,24 @@ OptimizeMIR(MIRGenerator *mir)
 
         if (!r.removeBetaNobes())
             return false;
         IonSpewPass("De-Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA De-Beta"))
             return false;
+
+        if (!r.truncate())
+            return false;
+        IonSpewPass("Truncate Doubles");
+        AssertExtendedGraphCoherency(graph);
+
+        if (mir->shouldCancel("Truncate Doubles"))
+            return false;
     }
 
     if (!EliminateDeadCode(mir, graph))
         return false;
     IonSpewPass("DCE");
     AssertExtendedGraphCoherency(graph);
 
     if (mir->shouldCancel("DCE"))
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -369,17 +369,16 @@ class TypeAnalyzer
         MPhi *phi = phiWorklist_.popCopy();
         phi->setNotInWorklist();
         return phi;
     }
 
     bool respecialize(MPhi *phi, MIRType type);
     bool propagateSpecialization(MPhi *phi);
     bool specializePhis();
-    bool specializeTruncatedInstructions();
     void replaceRedundantPhi(MPhi *phi);
     void adjustPhiInputs(MPhi *phi);
     bool adjustInputs(MDefinition *def);
     bool insertConversions();
 
   public:
     TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
       : mir(mir), graph(graph)
@@ -603,18 +602,16 @@ TypeAnalyzer::insertConversions()
     return true;
 }
 
 bool
 TypeAnalyzer::analyze()
 {
     if (!specializePhis())
         return false;
-    if (!specializeTruncatedInstructions())
-        return false;
     if (!insertConversions())
         return false;
     return true;
 }
 
 bool
 ion::ApplyTypeInformation(MIRGenerator *mir, MIRGraph &graph)
 {
@@ -1457,40 +1454,8 @@ LinearSum::print(Sprinter &sp) const
             sp.printf("%d*#%d", scale, id);
         }
     }
     if (constant_ > 0)
         sp.printf("+%d", constant_);
     else if (constant_ < 0)
         sp.printf("%d", constant_);
 }
-
-bool
-TypeAnalyzer::specializeTruncatedInstructions()
-{
-    // This specialization is a two step process: First we loop over the
-    // instruction stream forwards, marking all of the instructions that
-    // are computed purely from integers.  The theory is that we can observe
-    // values that don't fit into a 32 bit integer that can still be treated as
-    // integers.
-    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
-        if (mir->shouldCancel("recoverBigInts (forwards loop)"))
-            return false;
-
-        for (MDefinitionIterator iter(*block); iter; iter++) {
-            iter->recalculateBigInt();
-        }
-    }
-
-    // Now, if these adds of doubles-that-are-really-big-ints get truncated
-    // on all reads, then we know that we don't care that any of these operations
-    // produces a value that is not an integer.  To achieve this, loop over the instruction
-    // stream backwards, marking every instruction where all reads are operations that truncate
-    // If we have a double operation that is marked both "bigInt" and "truncated", then we can
-    // safely convert it into an integer instruction
-    for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); block++) {
-        if (mir->shouldCancel("Propagate Truncates (backwards loop)"))
-            return false;
-        for (MInstructionReverseIterator riter(block->rbegin()); riter != block->rend(); riter++)
-            riter->analyzeTruncateBackward();
-    }
-    return true;
-}
--- a/js/src/ion/JSONSpewer.cpp
+++ b/js/src/ion/JSONSpewer.cpp
@@ -251,23 +251,27 @@ JSONSpewer::spewMDef(MDefinition *def)
         integerValue(def->getOperand(i)->id());
     endList();
 
     beginListProperty("uses");
     for (MUseDefIterator use(def); use; use++)
         integerValue(use.def()->id());
     endList();
 
+    bool isTruncated = false;
+    if (def->isAdd() || def->isSub() || def->isMod() || def->isMul() || def->isDiv())
+        isTruncated = static_cast<MBinaryArithInstruction*>(def)->isTruncated();
+
     if (def->range()) {
         Sprinter sp(GetIonContext()->cx);
         sp.init();
         def->range()->print(sp);
-        stringProperty("type", "%s : %s", sp.string(), StringFromMIRType(def->type()));
+        stringProperty("type", "%s : %s%s", sp.string(), StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
     } else {
-        stringProperty("type", "%s", StringFromMIRType(def->type()));
+        stringProperty("type", "%s%s", StringFromMIRType(def->type()), (isTruncated ? " (t)" : ""));
     }
 
     if (def->isInstruction()) {
         if (MResumePoint *rp = def->toInstruction()->resumePoint())
             spewMResumePoint(rp);
     }
 
     endObject();
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -170,28 +170,21 @@ MDefinition::foldsTo(bool useValueNumber
 {
     // In the default case, there are no constants to fold.
     return this;
 }
 
 void
 MDefinition::analyzeEdgeCasesForward()
 {
-    return;
 }
 
 void
 MDefinition::analyzeEdgeCasesBackward()
 {
-    return;
-}
-void
-MDefinition::analyzeTruncateBackward()
-{
-    return;
 }
 
 static bool
 MaybeEmulatesUndefined(types::StackTypeSet *types, JSContext *cx)
 {
     if (!types->maybeObject())
         return false;
     return types->hasObjectFlags(cx, types::OBJECT_FLAG_EMULATES_UNDEFINED);
@@ -329,28 +322,16 @@ MConstant::New(const Value &v)
 
 MConstant::MConstant(const js::Value &vp)
   : value_(vp)
 {
     setResultType(MIRTypeFromValue(vp));
     setMovable();
 }
 
-void
-MConstant::analyzeTruncateBackward()
-{
-    if (js::ion::EdgeCaseAnalysis::AllUsesTruncate(this) &&
-        value_.isDouble() && isBigIntOutput())
-    {
-        // Truncate the double to int, since all uses truncates it.
-        value_.setInt32(ToInt32(value_.toDouble()));
-        setResultType(MIRType_Int32);
-    }
-}
-
 HashNumber
 MConstant::valueHash() const
 {
     // This disregards some state, since values are 64 bits. But for a hash,
     // it's completely acceptable.
     return (HashNumber)JSVAL_TO_IMPL(value_).asBits;
 }
 bool
@@ -886,17 +867,17 @@ MBinaryArithInstruction::foldsTo(bool us
         return rhs; // x op id => x
 
     return this;
 }
 
 bool
 MAbs::fallible() const
 {
-    return !range() || !range()->isFinite();
+    return !range() || !range()->isInt32();
 }
 
 MDefinition *
 MDiv::foldsTo(bool useValueNumbers)
 {
     if (specialization_ == MIRType_None)
         return this;
 
@@ -940,37 +921,16 @@ MDiv::analyzeEdgeCasesForward()
 
 void
 MDiv::analyzeEdgeCasesBackward()
 {
     if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
         setCanBeNegativeZero(false);
 }
 
-void
-MDiv::analyzeTruncateBackward()
-{
-    if (!isTruncated())
-        setTruncated(js::ion::EdgeCaseAnalysis::AllUsesTruncate(this));
-}
-
-bool
-MDiv::updateForReplacement(MDefinition *ins_)
-{
-    JS_ASSERT(ins_->isDiv());
-    MDiv *ins = ins_->toDiv();
-    // Since EdgeCaseAnalysis is not being run before GVN, its information does
-    // not need to be merged here.
-    if (isTruncated() && ins->isTruncated())
-        setTruncated(Max(isTruncated(), ins->isTruncated()));
-    else
-        setTruncated(0);
-    return true;
-}
-
 bool
 MDiv::fallible()
 {
     return !isTruncated();
 }
 
 static inline MDefinition *
 TryFold(MDefinition *original, MDefinition *replacement)
@@ -987,110 +947,43 @@ MMod::foldsTo(bool useValueNumbers)
         return this;
 
     if (MDefinition *folded = EvaluateConstantOperands(this))
         return folded;
 
     return this;
 }
 
-void
-MMod::analyzeTruncateBackward()
-{
-    if (!isTruncated())
-        setTruncated(js::ion::EdgeCaseAnalysis::AllUsesTruncate(this));
-}
-
-bool
-MMod::updateForReplacement(MDefinition *ins_)
-{
-    JS_ASSERT(ins_->isMod());
-    MMod *ins = ins_->toMod();
-    if (isTruncated() && ins->isTruncated())
-        setTruncated(Max(isTruncated(), ins->isTruncated()));
-    else
-        setTruncated(0);
-    return true;
-}
-
 bool
 MMod::fallible()
 {
     return !isTruncated();
 }
 
-void
-MAdd::analyzeTruncateBackward()
-{
-    if (!isTruncated())
-        setTruncated(js::ion::EdgeCaseAnalysis::AllUsesTruncate(this));
-    if (isTruncated() && isTruncated() < 20) {
-        // Super obvious optimization... If this operation is a double
-        // BUT it happens to look like a large precision int that eventually
-        // gets truncated, then just call it an int.
-        // This can arise if we have x+y | 0, and x and y are both INT_MAX,
-        // TI will observe an overflow, thus marking the addition as double-like
-        // but we'll have MTruncate(MAddD(toDouble(x), toDouble(y))), which we know
-        // we'll be able to convert to MAddI(x,y)
-        if (isBigInt_ && type() == MIRType_Double) {
-            specialization_ = MIRType_Int32;
-            setResultType(MIRType_Int32);
-        }
-    }
-}
-
-bool
-MAdd::updateForReplacement(MDefinition *ins_)
-{
-    JS_ASSERT(ins_->isAdd());
-    MAdd *ins = ins_->toAdd();
-    if (isTruncated() && ins->isTruncated())
-        setTruncated(Max(isTruncated(), ins->isTruncated()));
-    else
-        setTruncated(0);
-    return true;
-}
-
 bool
 MAdd::fallible()
 {
     // the add is fallible if range analysis does not say that it is finite, AND
-    // either the truncation analysis shows that there are non-truncated uses, or
-    // there are more than 20 operations before it gets truncated. 20 was chosen
-    // for two reasons. First, it is a nice sane number. Second, the largest int32
-    // can be (about) 2^31. The smallest integer that cannot be exactly represented
-    // as a double is 2^53 + 1  by doing something simple, like x = x + x, it takes
-    // 23 additions toget from 2^31 to 2^53 + 1. 20 is simply a conservative estimate of that.
-    return (!isTruncated() || isTruncated() > 20) && (!range() || !range()->isFinite());
-}
-
-void
-MSub::analyzeTruncateBackward()
-{
-    if (!isTruncated())
-        setTruncated(js::ion::EdgeCaseAnalysis::AllUsesTruncate(this));
-}
-
-bool
-MSub::updateForReplacement(MDefinition *ins_)
-{
-    JS_ASSERT(ins_->isSub());
-    MSub *ins = ins_->toSub();
-    if (isTruncated() && ins->isTruncated())
-        setTruncated(Max(isTruncated(), ins->isTruncated()));
-    else
-        setTruncated(0);
+    // either the truncation analysis shows that there are non-truncated uses.
+    if (isTruncated())
+        return false;
+    if (range() && range()->isInt32())
+        return false;
     return true;
 }
 
 bool
 MSub::fallible()
 {
     // see comment in MAdd::fallible()
-    return (!isTruncated() || isTruncated() > 20) && (!range() || !range()->isFinite());
+    if (isTruncated())
+        return false;
+    if (range() && range()->isInt32())
+        return false;
+    return true;
 }
 
 MDefinition *
 MMul::foldsTo(bool useValueNumbers)
 {
     MDefinition *out = MBinaryArithInstruction::foldsTo(useValueNumbers);
     if (out != this)
         return out;
@@ -1120,53 +1013,40 @@ MMul::analyzeEdgeCasesForward()
     }
 
     // If rhs is > 0, likewise.
     if (rhs()->isConstant()) {
         const js::Value &val = rhs()->toConstant()->value();
         if (val.isInt32() && val.toInt32() > 0)
             setCanBeNegativeZero(false);
     }
-
 }
 
 void
 MMul::analyzeEdgeCasesBackward()
 {
     if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
         setCanBeNegativeZero(false);
 }
 
-void
-MMul::analyzeTruncateBackward()
-{
-    if (!isPossibleTruncated() && js::ion::EdgeCaseAnalysis::AllUsesTruncate(this))
-        setPossibleTruncated(true);
-}
-
 bool
 MMul::updateForReplacement(MDefinition *ins_)
 {
-    JS_ASSERT(ins_->isMul());
     MMul *ins = ins_->toMul();
-    // setPossibleTruncated can reset the canBenegativeZero check,
-    // therefore first query the state, before setting the new state.
-    bool truncated = isPossibleTruncated() && ins->isPossibleTruncated();
     bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero();
-    setPossibleTruncated(truncated);
     setCanBeNegativeZero(negativeZero);
     return true;
 }
 
 bool
 MMul::canOverflow()
 {
-    if (implicitTruncate_)
+    if (isTruncated())
         return false;
-    return !range() || !range()->isFinite();
+    return !range() || !range()->isInt32();
 }
 
 void
 MBinaryArithInstruction::infer(const TypeOracle::BinaryTypes &b, JSContext *cx)
 {
     // Retrieve type information of lhs and rhs
     // Rhs is defaulted to int32 first,
     // because in some cases there is no rhs type information
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -297,17 +297,17 @@ class MDefinition : public MNode
     void setFlags(uint32_t flags) {
         flags_ |= flags;
     }
     virtual bool neverHoist() const { return false; }
   public:
     MDefinition()
       : id_(0),
         valueNumber_(NULL),
-        range_(),
+        range_(NULL),
         resultType_(MIRType_None),
         flags_(0),
         dependency_(NULL),
         trackedPc_(NULL)
     { }
 
     virtual Opcode op() const = 0;
     virtual const char *opName() const = 0;
@@ -333,17 +333,20 @@ class MDefinition : public MNode
     virtual HashNumber valueHash() const;
     virtual bool congruentTo(MDefinition* const &ins) const {
         return false;
     }
     bool congruentIfOperandsEqual(MDefinition * const &ins) const;
     virtual MDefinition *foldsTo(bool useValueNumbers);
     virtual void analyzeEdgeCasesForward();
     virtual void analyzeEdgeCasesBackward();
-    virtual void analyzeTruncateBackward();
+
+    virtual bool truncate();
+    virtual bool isOperandTruncated(size_t index) const;
+
     bool earlyAbortCheck();
 
     // Compute an absolute or symbolic range for the value of this node.
     // Ranges are only computed for definitions whose type is int32.
     virtual void computeRange() {
     }
 
     MNode::Kind kind() const {
@@ -493,24 +496,16 @@ class MDefinition : public MNode
     virtual bool mightAlias(MDefinition *store) {
         // Return whether this load may depend on the specified store, given
         // that the alias sets intersect. This may be refined to exclude
         // possible aliasing in cases where alias set flags are too imprecise.
         JS_ASSERT(!isEffectful() && store->isEffectful());
         JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags());
         return true;
     }
-    // This indicates if this instruction is "integral at heart".  This will
-    // be the case if
-    // a) its result type is int32
-    // b) it is an instruction that is very likely to produce an integer or integer-truncatable
-    //    result (add, mul, sub), and both of its inputs are ints. (currently only implemented for add)
-    virtual bool isBigIntOutput() { return resultType_ == MIRType_Int32; }
-    virtual void recalculateBigInt() {}
-
 };
 
 // An MUseDefIterator walks over uses in a definition, skipping any use that is
 // not a definition. Items from the use list must not be deleted during
 // iteration.
 class MUseDefIterator
 {
     MDefinition *def_;
@@ -710,37 +705,18 @@ class MConstant : public MNullaryInstruc
 
     HashNumber valueHash() const;
     bool congruentTo(MDefinition * const &ins) const;
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
-    void analyzeTruncateBackward();
-
-    // Returns true if constant is integer between -2^33 & 2^33,
-    // Max cap could be 2^53, if not for the 20 additions hack.
-    bool isBigIntOutput() {
-        if (value_.isInt32())
-            return true;
-        if (value_.isDouble()) {
-            double value = value_.toDouble();
-            int64_t valint = value;
-            int64_t max = 1LL<<33;
-            if (double(valint) != value)
-                return false;
-            if (valint < 0)
-                valint = -valint;
-            return valint < max;
-        }
-        return false;
-    }
-
     void computeRange();
+    bool truncate();
 };
 
 class MParameter : public MNullaryInstruction
 {
     int32_t index_;
     const types::StackTypeSet *typeSet_;
 
   public:
@@ -2094,16 +2070,20 @@ class MToDouble
         return getOperand(0);
     }
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
+
+    void computeRange();
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
 };
 
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
 class MToInt32 : public MUnaryInstruction
 {
     bool canBeNegativeZero_;
@@ -2141,16 +2121,17 @@ class MToInt32 : public MUnaryInstructio
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
+    void computeRange();
 };
 
 // Converts a value or typed input to a truncated int32, for use with bitwise
 // operations. This is an infallible ValueToECMAInt32.
 class MTruncateToInt32 : public MUnaryInstruction
 {
     MTruncateToInt32(MDefinition *def)
       : MUnaryInstruction(def)
@@ -2173,16 +2154,19 @@ class MTruncateToInt32 : public MUnaryIn
     MDefinition *foldsTo(bool useValueNumbers);
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
+
+    void computeRange();
+    bool isOperandTruncated(size_t index) const;
 };
 
 // Converts any type to a string
 class MToString : public MUnaryInstruction
 {
     MToString(MDefinition *def)
       : MUnaryInstruction(def)
     {
@@ -2333,16 +2317,18 @@ class MBinaryBitwiseInstruction
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
+
+    bool isOperandTruncated(size_t index) const;
 };
 
 class MBitAnd : public MBinaryBitwiseInstruction
 {
     MBitAnd(MDefinition *left, MDefinition *right)
       : MBinaryBitwiseInstruction(left, right)
     { }
 
@@ -2506,19 +2492,30 @@ class MUrsh : public MShiftInstruction
         return canOverflow();
     }
 };
 
 class MBinaryArithInstruction
   : public MBinaryInstruction,
     public ArithPolicy
 {
+    // Implicit truncate flag is set by the truncate backward range analysis
+    // optimization phase, and by asm.js pre-processing. It is used in
+    // 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.
+    bool implicitTruncate_;
+
   public:
     MBinaryArithInstruction(MDefinition *left, MDefinition *right)
-      : MBinaryInstruction(left, right)
+      : MBinaryInstruction(left, right),
+        implicitTruncate_(false)
     {
         setMovable();
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     MIRType specialization() const {
@@ -2539,16 +2536,23 @@ class MBinaryArithInstruction
     bool congruentTo(MDefinition *const &ins) const {
         return MBinaryInstruction::congruentTo(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
+
+    bool isTruncated() const {
+        return implicitTruncate_;
+    }
+    void setTruncated(bool truncate) {
+        implicitTruncate_ = truncate;
+    }
 };
 
 class MMinMax
   : public MBinaryInstruction,
     public ArithPolicy
 {
     bool isMax_;
 
@@ -2794,128 +2798,88 @@ class MMathFunction
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MAdd : public MBinaryArithInstruction
 {
-    int implicitTruncate_;
     // Is this instruction really an int at heart?
-    bool isBigInt_;
     MAdd(MDefinition *left, MDefinition *right)
-      : MBinaryArithInstruction(left, right),
-        implicitTruncate_(0),
-        isBigInt_(left->isBigIntOutput() && right->isBigIntOutput())
+      : MBinaryArithInstruction(left, right)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Add)
     static MAdd *New(MDefinition *left, MDefinition *right) {
         return new MAdd(left, right);
     }
-    void analyzeTruncateBackward();
-
-    int isTruncated() const {
-        return implicitTruncate_;
-    }
-    void setTruncated(int truncate) {
-        implicitTruncate_ = truncate;
-    }
-    bool updateForReplacement(MDefinition *ins);
+
     double getIdentity() {
         return 0;
     }
 
     bool fallible();
     void computeRange();
-    // This is an add, so the return value is from only
-    // integer sources if we know we return an int32
-    // or it has been explicitly marked as being a large int.
-    bool isBigIntOutput() {
-        return (type() == MIRType_Int32) || isBigInt_;
-    }
-    // An add will produce a big int if both of its sources are big ints.
-    void recalculateBigInt() {
-        isBigInt_ = (lhs()->isBigIntOutput() && rhs()->isBigIntOutput());
-    }
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
 };
 
 class MSub : public MBinaryArithInstruction
 {
-    int implicitTruncate_;
     MSub(MDefinition *left, MDefinition *right)
-      : MBinaryArithInstruction(left, right),
-        implicitTruncate_(0)
+      : MBinaryArithInstruction(left, right)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Sub)
     static MSub *New(MDefinition *left, MDefinition *right) {
         return new MSub(left, right);
     }
 
-    void analyzeTruncateBackward();
-    int isTruncated() const {
-        return implicitTruncate_;
-    }
-    void setTruncated(int truncate) {
-        implicitTruncate_ = truncate;
-    }
-    bool updateForReplacement(MDefinition *ins);
-
     double getIdentity() {
         return 0;
     }
 
     bool fallible();
     void computeRange();
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
 };
 
 class MMul : public MBinaryArithInstruction
 {
   public:
     enum Mode {
         Normal,
         Integer
     };
 
   private:
     // Annotation the result could be a negative zero
     // and we need to guard this during execution.
     bool canBeNegativeZero_;
 
-    // Annotation the result of this Mul is only used in int32 domain
-    // and we could possible truncate the result.
-    bool possibleTruncate_;
-
-    // Annotation the Mul can truncate. This is only set after range analysis,
-    // because the result could be in the imprecise double range.
-    // In that case the truncated result isn't correct.
-    bool implicitTruncate_;
-
     Mode mode_;
 
     MMul(MDefinition *left, MDefinition *right, MIRType type, Mode mode)
       : MBinaryArithInstruction(left, right),
         canBeNegativeZero_(true),
-        possibleTruncate_(false),
-        implicitTruncate_(false),
         mode_(mode)
     {
         if (mode == Integer) {
             // This implements the required behavior for Math.imul, which
             // can never fail and always truncates its output to int32.
             canBeNegativeZero_ = false;
-            possibleTruncate_ = implicitTruncate_ = true;
+            setTruncated(true);
         }
         JS_ASSERT_IF(mode != Integer, mode == Normal);
 
         if (type != MIRType_Value)
             specialization_ = type;
         setResultType(type);
     }
 
@@ -2926,17 +2890,16 @@ class MMul : public MBinaryArithInstruct
     }
     static MMul *New(MDefinition *left, MDefinition *right, MIRType type, Mode mode = Normal) {
         return new MMul(left, right, type, mode);
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
     void analyzeEdgeCasesForward();
     void analyzeEdgeCasesBackward();
-    void analyzeTruncateBackward();
 
     double getIdentity() {
         return 1;
     }
 
     bool canOverflow();
 
     bool canBeNegativeZero() {
@@ -2948,48 +2911,33 @@ class MMul : public MBinaryArithInstruct
 
     bool updateForReplacement(MDefinition *ins);
 
     bool fallible() {
         return canBeNegativeZero_ || canOverflow();
     }
 
     void computeRange();
-
-    bool isPossibleTruncated() const {
-        return possibleTruncate_;
-    }
-
-    void setPossibleTruncated(bool truncate) {
-        possibleTruncate_ = truncate;
-
-        // We can remove the negative zero check, because op if it is only used truncated.
-        // The "Possible" in the function name means that we are not sure,
-        // that "integer mul and disregarding overflow" == "double mul and ToInt32"
-        // Note: when removing truncated state, we have to add negative zero check again,
-        // because we are not sure if it was removed by this or other passes.
-        canBeNegativeZero_ = !truncate;
-    }
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
 
     Mode mode() { return mode_; }
 };
 
 class MDiv : public MBinaryArithInstruction
 {
     bool canBeNegativeZero_;
     bool canBeNegativeOverflow_;
     bool canBeDivideByZero_;
-    int implicitTruncate_;
 
     MDiv(MDefinition *left, MDefinition *right, MIRType type)
       : MBinaryArithInstruction(left, right),
         canBeNegativeZero_(true),
         canBeNegativeOverflow_(true),
-        canBeDivideByZero_(true),
-        implicitTruncate_(0)
+        canBeDivideByZero_(true)
     {
         if (type != MIRType_Value)
             specialization_ = type;
         setResultType(type);
     }
 
   public:
     INSTRUCTION_HEADER(Div)
@@ -2998,84 +2946,66 @@ class MDiv : public MBinaryArithInstruct
     }
     static MDiv *New(MDefinition *left, MDefinition *right, MIRType type) {
         return new MDiv(left, right, type);
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
     void analyzeEdgeCasesForward();
     void analyzeEdgeCasesBackward();
-    void analyzeTruncateBackward();
 
     double getIdentity() {
         JS_NOT_REACHED("not used");
         return 1;
     }
 
-    int isTruncated() const {
-        return implicitTruncate_;
-    }
-    void setTruncated(int truncate) {
-        implicitTruncate_ = truncate;
-    }
-
     bool canBeNegativeZero() {
         return canBeNegativeZero_;
     }
     void setCanBeNegativeZero(bool negativeZero) {
         canBeNegativeZero_ = negativeZero;
     }
 
     bool canBeNegativeOverflow() {
         return canBeNegativeOverflow_;
     }
 
     bool canBeDivideByZero() {
         return canBeDivideByZero_;
     }
 
-    bool updateForReplacement(MDefinition *ins);
     bool fallible();
+    bool truncate();
 };
 
 class MMod : public MBinaryArithInstruction
 {
-    int implicitTruncate_;
-
     MMod(MDefinition *left, MDefinition *right)
-      : MBinaryArithInstruction(left, right),
-        implicitTruncate_(0)
+      : MBinaryArithInstruction(left, right)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Mod)
     static MMod *New(MDefinition *left, MDefinition *right) {
         return new MMod(left, right);
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
-    void analyzeTruncateBackward();
 
     double getIdentity() {
         JS_NOT_REACHED("not used");
         return 1;
     }
 
-    int isTruncated() const {
-        return implicitTruncate_;
-    }
-    void setTruncated(int truncate) {
-        implicitTruncate_ = truncate;
-    }
-
-    bool updateForReplacement(MDefinition *ins);
+    bool fallible();
+
     void computeRange();
-    bool fallible();
+    bool truncate();
 };
 
 class MConcat
   : public MBinaryInstruction,
     public BinaryStringPolicy
 {
     MConcat(MDefinition *left, MDefinition *right)
       : MBinaryInstruction(left, right)
--- a/js/src/ion/RangeAnalysis.cpp
+++ b/js/src/ion/RangeAnalysis.cpp
@@ -1,17 +1,20 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=99:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include <math.h>
 #include <stdio.h>
 
+#include "vm/NumericConversions.h"
+
 #include "Ion.h"
 #include "IonAnalysis.h"
 #include "MIR.h"
 #include "MIRGraph.h"
 #include "RangeAnalysis.h"
 #include "IonSpewer.h"
 
 using namespace js;
@@ -158,30 +161,38 @@ RangeAnalysis::addBetaNobes()
                 smaller = left;
                 greater = right;
             } else if (jsop == JSOP_GT) {
                 smaller = right;
                 greater = left;
             }
             if (smaller && greater) {
                 MBeta *beta;
-                beta = MBeta::New(smaller, new Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1));
+                beta = MBeta::New(smaller, new Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1,
+                                                     smaller->type() != MIRType_Int32,
+                                                     Range::MaxDoubleExponent));
                 block->insertBefore(*block->begin(), beta);
                 replaceDominatedUsesWith(smaller, beta, block);
-                beta = MBeta::New(greater, new Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX));
+                IonSpew(IonSpew_Range, "Adding beta node for smaller %d", smaller->id());
+                beta = MBeta::New(greater, new Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX,
+                                                     greater->type() != MIRType_Int32,
+                                                     Range::MaxDoubleExponent));
                 block->insertBefore(*block->begin(), beta);
                 replaceDominatedUsesWith(greater, beta, block);
+                IonSpew(IonSpew_Range, "Adding beta node for greater %d", greater->id());
             }
             continue;
         }
 
         JS_ASSERT(val);
 
 
         Range comp;
+        if (val->type() == MIRType_Int32)
+            comp.setInt32();
         switch (jsop) {
           case JSOP_LE:
             comp.setUpper(bound);
             break;
           case JSOP_LT:
             if (!SafeSub(bound, 1, &bound))
                 break;
             comp.setUpper(bound);
@@ -246,16 +257,22 @@ SymbolicBound::print(Sprinter &sp) const
 }
 
 void
 Range::print(Sprinter &sp) const
 {
     JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
     JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
 
+    // Real or Natural subset.
+    if (decimal_)
+        sp.printf("R");
+    else
+        sp.printf("N");
+
     sp.printf("[");
 
     if (lower_infinite_)
         sp.printf("-inf");
     else
         sp.printf("%d", lower_);
     if (symbolicLower_) {
         sp.printf(" {");
@@ -271,16 +288,17 @@ Range::print(Sprinter &sp) const
         sp.printf("%d", upper_);
     if (symbolicUpper_) {
         sp.printf(" {");
         symbolicUpper_->print(sp);
         sp.printf("}");
     }
 
     sp.printf("]");
+    sp.printf(" (%db)", numBits());
 }
 
 Range *
 Range::intersect(const Range *lhs, const Range *rhs, bool *emptyRange)
 {
     *emptyRange = false;
 
     if (!lhs && !rhs)
@@ -288,17 +306,19 @@ Range::intersect(const Range *lhs, const
 
     if (!lhs)
         return new Range(*rhs);
     if (!rhs)
         return new Range(*lhs);
 
     Range *r = new Range(
         Max(lhs->lower_, rhs->lower_),
-        Min(lhs->upper_, rhs->upper_));
+        Min(lhs->upper_, rhs->upper_),
+        lhs->decimal_ && rhs->decimal_,
+        Min(lhs->max_exponent_, rhs->max_exponent_));
 
     r->lower_infinite_ = lhs->lower_infinite_ && rhs->lower_infinite_;
     r->upper_infinite_ = lhs->upper_infinite_ && rhs->upper_infinite_;
 
     // :TODO: This information could be used better. If upper < lower, then we
     // have conflicting constraints. Consider:
     //
     // if (x < 0) {
@@ -318,86 +338,88 @@ Range::intersect(const Range *lhs, const
     }
 
     return r;
 }
 
 void
 Range::unionWith(const Range *other)
 {
+   lower_infinite_ |= other->lower_infinite_;
+   upper_infinite_ |= other->upper_infinite_;
+   decimal_ |= other->decimal_;
+   max_exponent_ = Max(max_exponent_, other->max_exponent_);
    setLower(Min(lower_, other->lower_));
-   lower_infinite_ |= other->lower_infinite_;
    setUpper(Max(upper_, other->upper_));
-   upper_infinite_ |= other->upper_infinite_;
 }
 
 static const Range emptyRange;
 
-static inline void
-EnsureRange(const Range **prange)
+Range::Range(const MDefinition *def)
+  : symbolicLower_(NULL),
+    symbolicUpper_(NULL)
 {
-    if (!*prange)
-        *prange = &emptyRange;
+    const Range *other = def->range();
+    if (!other)
+        other = &emptyRange;
+
+    lower_ = other->lower_;
+    lower_infinite_ = other->lower_infinite_;
+    upper_ = other->upper_;
+    upper_infinite_ = other->upper_infinite_;
+    decimal_ = other->decimal_;
+    max_exponent_ = other->max_exponent_;
+
+    if (def->type() == MIRType_Int32)
+        truncate();
+}
+
+const int64_t RANGE_INF_MAX = (int64_t) JSVAL_INT_MAX + 1;
+const int64_t RANGE_INF_MIN = (int64_t) JSVAL_INT_MIN - 1;
+
+static inline bool
+HasInfinite(const Range *lhs, const Range *rhs)
+{
+    return lhs->isLowerInfinite() || lhs->isUpperInfinite() ||
+           rhs->isLowerInfinite() || rhs->isUpperInfinite();
 }
 
 Range *
 Range::add(const Range *lhs, const Range *rhs)
 {
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-    return new Range(
-        (int64_t)lhs->lower_ + (int64_t)rhs->lower_,
-        (int64_t)lhs->upper_ + (int64_t)rhs->upper_);
+    int64_t l = (int64_t) lhs->lower_ + (int64_t) rhs->lower_;
+    if (lhs->isLowerInfinite() || rhs->isLowerInfinite())
+        l = RANGE_INF_MIN;
+
+    int64_t h = (int64_t) lhs->upper_ + (int64_t) rhs->upper_;
+    if (lhs->isUpperInfinite() || rhs->isUpperInfinite())
+        h = RANGE_INF_MAX;
+
+    return new Range(l, h, lhs->isDecimal() || rhs->isDecimal(),
+                     Max(lhs->exponent(), rhs->exponent()) + 1);
 }
 
 Range *
 Range::sub(const Range *lhs, const Range *rhs)
 {
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-    return new Range(
-        (int64_t)lhs->lower_ - (int64_t)rhs->upper_,
-        (int64_t)lhs->upper_ - (int64_t)rhs->lower_);
-}
+    int64_t l = (int64_t) lhs->lower_ - (int64_t) rhs->upper_;
+    if (lhs->isLowerInfinite() || rhs->isUpperInfinite())
+        l = RANGE_INF_MIN;
 
-/* static */ Range *
-Range::Truncate(int64_t l, int64_t h)
-{
-    Range *ret = new Range(l, h);
-    if (!ret->isFinite()) {
-        ret->makeLowerInfinite();
-        ret->makeUpperInfinite();
-    }
-    return ret;
-}
+    int64_t h = (int64_t) lhs->upper_ - (int64_t) rhs->lower_;
+    if (lhs->isUpperInfinite() || rhs->isLowerInfinite())
+        h = RANGE_INF_MAX;
 
-Range *
-Range::addTruncate(const Range *lhs, const Range *rhs)
-{
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-    return Truncate((int64_t)lhs->lower_ + (int64_t)rhs->lower_,
-                    (int64_t)lhs->upper_ + (int64_t)rhs->upper_);
-}
-
-Range *
-Range::subTruncate(const Range *lhs, const Range *rhs)
-{
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-    return Truncate((int64_t)lhs->lower_ - (int64_t)rhs->upper_,
-                    (int64_t)lhs->upper_ - (int64_t)rhs->lower_);
+    return new Range(l, h, lhs->isDecimal() || rhs->isDecimal(),
+                     Max(lhs->exponent(), rhs->exponent()) + 1);
 }
 
 Range *
 Range::and_(const Range *lhs, const Range *rhs)
 {
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-
     int64_t lower;
     int64_t upper;
 
     // If both numbers can be negative, result can be negative in the whole range
     if (lhs->lower_ < 0 && rhs->lower_ < 0) {
         lower = INT_MIN;
         upper = Max(lhs->upper_, rhs->upper_);
         return new Range(lower, upper);
@@ -418,73 +440,51 @@ Range::and_(const Range *lhs, const Rang
         upper = lhs->upper_;
 
     return new Range(lower, upper);
 }
 
 Range *
 Range::mul(const Range *lhs, const Range *rhs)
 {
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
+    bool decimal = lhs->isDecimal() || rhs->isDecimal();
+    uint16_t exponent = lhs->numBits() + rhs->numBits() - 1;
+    if (HasInfinite(lhs, rhs))
+        return new Range(RANGE_INF_MIN, RANGE_INF_MAX, decimal, exponent);
     int64_t a = (int64_t)lhs->lower_ * (int64_t)rhs->lower_;
     int64_t b = (int64_t)lhs->lower_ * (int64_t)rhs->upper_;
     int64_t c = (int64_t)lhs->upper_ * (int64_t)rhs->lower_;
     int64_t d = (int64_t)lhs->upper_ * (int64_t)rhs->upper_;
     return new Range(
         Min( Min(a, b), Min(c, d) ),
-        Max( Max(a, b), Max(c, d) ));
+        Max( Max(a, b), Max(c, d) ),
+        decimal, exponent);
 }
 
 Range *
 Range::shl(const Range *lhs, int32_t c)
 {
-    EnsureRange(&lhs);
     int32_t shift = c & 0x1f;
     return new Range(
         (int64_t)lhs->lower_ << shift,
         (int64_t)lhs->upper_ << shift);
 }
 
 Range *
 Range::shr(const Range *lhs, int32_t c)
 {
-    EnsureRange(&lhs);
     int32_t shift = c & 0x1f;
     return new Range(
         (int64_t)lhs->lower_ >> shift,
         (int64_t)lhs->upper_ >> shift);
 }
 
 bool
-Range::precisionLossMul(const Range *lhs, const Range *rhs)
-{
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-    int64_t loss  = 1LL<<53; // result must be lower than 2^53
-    int64_t a = (int64_t)lhs->lower_ * (int64_t)rhs->lower_;
-    int64_t b = (int64_t)lhs->lower_ * (int64_t)rhs->upper_;
-    int64_t c = (int64_t)lhs->upper_ * (int64_t)rhs->lower_;
-    int64_t d = (int64_t)lhs->upper_ * (int64_t)rhs->upper_;
-    int64_t lower = Min( Min(a, b), Min(c, d) );
-    int64_t upper = Max( Max(a, b), Max(c, d) );
-    if (lower < 0)
-        lower = -lower;
-    if (upper < 0)
-        upper = -upper;
-
-    return lower > loss || upper > loss;
-}
-
-bool
 Range::negativeZeroMul(const Range *lhs, const Range *rhs)
 {
-    EnsureRange(&lhs);
-    EnsureRange(&rhs);
-
     // Both values are positive
     if (lhs->lower_ >= 0 && rhs->lower_ >= 0)
         return false;
 
     // Both values are negative (non zero)
     if (lhs->upper_ < 0 && rhs->upper_ < 0)
         return false;
 
@@ -497,35 +497,39 @@ Range::negativeZeroMul(const Range *lhs,
 
 bool
 Range::update(const Range *other)
 {
     bool changed =
         lower_ != other->lower_ ||
         lower_infinite_ != other->lower_infinite_ ||
         upper_ != other->upper_ ||
-        upper_infinite_ != other->upper_infinite_;
+        upper_infinite_ != other->upper_infinite_ ||
+        decimal_ != other->decimal_ ||
+        max_exponent_ != other->max_exponent_;
     if (changed) {
         lower_ = other->lower_;
         lower_infinite_ = other->lower_infinite_;
         upper_ = other->upper_;
         upper_infinite_ = other->upper_infinite_;
+        decimal_ = other->decimal_;
+        max_exponent_ = other->max_exponent_;
     }
 
     return changed;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Range Computation for MIR Nodes
 ///////////////////////////////////////////////////////////////////////////////
 
 void
 MPhi::computeRange()
 {
-    if (type() != MIRType_Int32)
+    if (type() != MIRType_Int32 && type() != MIRType_Double)
         return;
 
     Range *range = NULL;
     JS_ASSERT(getOperand(0)->op() != MDefinition::Op_OsrValue);
     for (size_t i = 0; i < numOperands(); i++) {
         if (getOperand(i)->block()->earlyAbort()) {
             IonSpew(IonSpew_Range, "Ignoring unreachable input %d", getOperand(i)->id());
             continue;
@@ -551,18 +555,68 @@ MPhi::computeRange()
 
     if (block()->isLoopHeader()) {
     }
 }
 
 void
 MConstant::computeRange()
 {
-    if (type() == MIRType_Int32)
+    if (type() == MIRType_Int32) {
         setRange(new Range(value().toInt32(), value().toInt32()));
+        return;
+    }
+
+    if (type() != MIRType_Double)
+        return;
+
+    double d = value().toDouble();
+    int exp = Range::MaxDoubleExponent;
+
+    // NaN is estimated as a Double which covers everything.
+    if (MOZ_DOUBLE_IS_NaN(d)) {
+        setRange(new Range(RANGE_INF_MIN, RANGE_INF_MAX, true, exp));
+        return;
+    }
+
+    // Infinity is used to set both lower and upper to the range boundaries.
+    if (MOZ_DOUBLE_IS_INFINITE(d)) {
+        if (MOZ_DOUBLE_IS_NEGATIVE(d))
+            setRange(new Range(RANGE_INF_MIN, RANGE_INF_MIN, false, exp));
+        else
+            setRange(new Range(RANGE_INF_MAX, RANGE_INF_MAX, false, exp));
+        return;
+    }
+
+    // Extract the exponent, to approximate it with the range analysis.
+    exp = MOZ_DOUBLE_EXPONENT(d);
+    if (exp < 0) {
+        // This double only has a decimal part.
+        if (MOZ_DOUBLE_IS_NEGATIVE(d))
+            setRange(new Range(-1, 0, true, 0));
+        else
+            setRange(new Range(0, 1, true, 0));
+    } else if (exp < Range::MaxTruncatableExponent) {
+        // Extract the integral part.
+        int64_t integral = ToInt64(d);
+        // Extract the decimal part.
+        double rest = d - (double) integral;
+        // Estimate the smallest integral boundaries.
+        //   Safe double comparisons, because there is no precision loss.
+        int64_t l = integral - ((rest < 0) ? 1 : 0);
+        int64_t h = integral + ((rest > 0) ? 1 : 0);
+        setRange(new Range(l, h, (rest != 0), exp));
+    } else {
+        // This double has a precision loss. This also mean that it cannot
+        // encode any decimals.
+        if (MOZ_DOUBLE_IS_NEGATIVE(d))
+            setRange(new Range(RANGE_INF_MIN, RANGE_INF_MIN, false, exp));
+        else
+            setRange(new Range(RANGE_INF_MAX, RANGE_INF_MAX, false, exp));
+    }
 }
 
 void
 MCharCodeAt::computeRange()
 {
     setRange(new Range(0, 65535)); //ECMA 262 says that the integer will be
                                    //non-negative and at most 65535.
 }
@@ -571,109 +625,128 @@ void
 MClampToUint8::computeRange()
 {
     setRange(new Range(0, 255));
 }
 
 void
 MBitAnd::computeRange()
 {
-    Range *left = getOperand(0)->range();
-    Range *right = getOperand(1)->range();
-    setRange(Range::and_(left, right));
+    Range left(getOperand(0));
+    Range right(getOperand(1));
+    setRange(Range::and_(&left, &right));
 }
 
 void
 MLsh::computeRange()
 {
     MDefinition *right = getOperand(1);
     if (!right->isConstant())
         return;
 
     int32_t c = right->toConstant()->value().toInt32();
-    const Range *other = getOperand(0)->range();
-    setRange(Range::shl(other, c));
+    Range other(getOperand(0));
+    setRange(Range::shl(&other, c));
 }
 
 void
 MRsh::computeRange()
 {
     MDefinition *right = getOperand(1);
     if (!right->isConstant())
         return;
 
     int32_t c = right->toConstant()->value().toInt32();
-    Range *other = getOperand(0)->range();
-    setRange(Range::shr(other, c));
+    Range other(getOperand(0));
+    setRange(Range::shr(&other, c));
 }
 
 void
 MAbs::computeRange()
 {
-    if (specialization_ != MIRType_Int32)
+    if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
         return;
 
-    const Range *other = getOperand(0)->range();
-    EnsureRange(&other);
+    Range other(getOperand(0));
 
     Range *range = new Range(0,
-                             Max(Range::abs64((int64_t)other->lower()),
-                                 Range::abs64((int64_t)other->upper())));
+                             Max(Range::abs64((int64_t) other.lower()),
+                                 Range::abs64((int64_t) other.upper())),
+                             other.isDecimal(),
+                             other.exponent());
     setRange(range);
 }
 
 void
 MAdd::computeRange()
 {
-    if (specialization() != MIRType_Int32)
+    if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
-    Range *left = getOperand(0)->range();
-    Range *right = getOperand(1)->range();
-    Range *next = isTruncated() ? Range::addTruncate(left,right) : Range::add(left, right);
+    Range left(getOperand(0));
+    Range right(getOperand(1));
+    Range *next = Range::add(&left, &right);
     setRange(next);
 }
 
 void
 MSub::computeRange()
 {
-    if (specialization() != MIRType_Int32)
+    if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
-    Range *left = getOperand(0)->range();
-    Range *right = getOperand(1)->range();
-    Range *next = isTruncated() ? Range::subTruncate(left,right) : Range::sub(left, right);
+    Range left(getOperand(0));
+    Range right(getOperand(1));
+    Range *next = Range::sub(&left, &right);
     setRange(next);
 }
 
 void
 MMul::computeRange()
 {
-    if (specialization() != MIRType_Int32)
+    if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
-    Range *left = getOperand(0)->range();
-    Range *right = getOperand(1)->range();
-    if (!implicitTruncate_ && isPossibleTruncated())
-        implicitTruncate_ = !Range::precisionLossMul(left, right);
+    Range left(getOperand(0));
+    Range right(getOperand(1));
     if (canBeNegativeZero())
-        canBeNegativeZero_ = Range::negativeZeroMul(left, right);
-    setRange(Range::mul(left, right));
+        canBeNegativeZero_ = Range::negativeZeroMul(&left, &right);
+    setRange(Range::mul(&left, &right));
 }
 
 void
 MMod::computeRange()
 {
-    if (specialization() != MIRType_Int32)
+    if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
-    const Range *rhs = getOperand(1)->range();
-    EnsureRange(&rhs);
-    int64_t a = Range::abs64((int64_t)rhs->lower());
-    int64_t b = Range::abs64((int64_t)rhs->upper());
+    Range lhs(getOperand(0));
+    Range rhs(getOperand(1));
+    int64_t a = Range::abs64((int64_t) rhs.lower());
+    int64_t b = Range::abs64((int64_t) rhs.upper());
     if (a == 0 && b == 0)
         return;
     int64_t bound = Max(1-a, b-1);
-    setRange(new Range(-bound, bound));
+    setRange(new Range(-bound, bound, lhs.isDecimal() || rhs.isDecimal()));
+}
+
+void
+MToDouble::computeRange()
+{
+    setRange(new Range(getOperand(0)));
+}
+
+void
+MTruncateToInt32::computeRange()
+{
+    Range input(getOperand(0));
+    setRange(new Range(input.lower(), input.upper()));
+}
+
+void
+MToInt32::computeRange()
+{
+    Range input(getOperand(0));
+    setRange(new Range(input.lower(), input.upper()));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Range Analysis
 ///////////////////////////////////////////////////////////////////////////////
 
 void
 RangeAnalysis::markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current)
@@ -1119,8 +1192,282 @@ RangeAnalysis::analyze()
         }
 
         if (block->isLoopHeader())
             analyzeLoop(block);
     }
 
     return true;
 }
+
+///////////////////////////////////////////////////////////////////////////////
+// Range based Truncation
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Range::truncate()
+{
+    if (isInt32())
+        return;
+    int64_t l = isLowerInfinite() ? JSVAL_INT_MIN : lower();
+    int64_t h = isUpperInfinite() ? JSVAL_INT_MAX : upper();
+    set(l, h, false, 32);
+}
+
+bool
+MDefinition::truncate()
+{
+    // No procedure defined for truncating this instruction.
+    return false;
+}
+
+bool
+MConstant::truncate()
+{
+    if (!value_.isDouble())
+        return false;
+
+    // Truncate the double to int, since all uses truncates it.
+    value_.setInt32(ToInt32(value_.toDouble()));
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->truncate();
+    return true;
+}
+
+bool
+MAdd::truncate()
+{
+    // Remember analysis, needed for fallible checks.
+    setTruncated(true);
+
+    // Modify the instruction if needed.
+    if (type() != MIRType_Double)
+        return false;
+
+    specialization_ = MIRType_Int32;
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->truncate();
+    return true;
+}
+
+bool
+MSub::truncate()
+{
+    // Remember analysis, needed for fallible checks.
+    setTruncated(true);
+
+    // Modify the instruction if needed.
+    if (type() != MIRType_Double)
+        return false;
+
+    specialization_ = MIRType_Int32;
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->truncate();
+    return true;
+}
+
+bool
+MMul::truncate()
+{
+    // Remember analysis, needed to remove negative zero checks.
+    setTruncated(true);
+
+    // Modify the instruction.
+    bool truncated = type() == MIRType_Int32;
+    if (type() == MIRType_Double) {
+        specialization_ = MIRType_Int32;
+        setResultType(MIRType_Int32);
+        truncated = true;
+        JS_ASSERT(range());
+    }
+
+    if (truncated && range()) {
+        range()->truncate();
+        setTruncated(true);
+        setCanBeNegativeZero(false);
+    }
+
+    return truncated;
+}
+
+bool
+MDiv::truncate()
+{
+    // Remember analysis, needed to remove negative zero checks.
+    setTruncated(true);
+
+    // No modifications.
+    return false;
+}
+
+bool
+MMod::truncate()
+{
+    // Remember analysis, needed to remove negative zero checks.
+    setTruncated(true);
+
+    // No modifications.
+    return false;
+}
+
+bool
+MToDouble::truncate()
+{
+    JS_ASSERT(type() == MIRType_Double);
+
+    // We use the return type to flag that this MToDouble sould be replaced by a
+    // MTruncateToInt32 when modifying the graph.
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->truncate();
+
+    return true;
+}
+
+bool
+MDefinition::isOperandTruncated(size_t index) const
+{
+    return false;
+}
+
+bool
+MTruncateToInt32::isOperandTruncated(size_t index) const
+{
+    return true;
+}
+
+bool
+MBinaryBitwiseInstruction::isOperandTruncated(size_t index) const
+{
+    return true;
+}
+
+bool
+MAdd::isOperandTruncated(size_t index) const
+{
+    return isTruncated();
+}
+
+bool
+MSub::isOperandTruncated(size_t index) const
+{
+    return isTruncated();
+}
+
+bool
+MMul::isOperandTruncated(size_t index) const
+{
+    return isTruncated();
+}
+
+bool
+MToDouble::isOperandTruncated(size_t index) const
+{
+    // The return type is used to flag that we are replacing this Double by a
+    // Truncate of its operand if needed.
+    return type() == MIRType_Int32;
+}
+
+// Ensure that all observables (non-resume point) uses can work with a truncated
+// version of the |candidate|'s result.
+static bool
+AllUsesTruncate(MInstruction *candidate)
+{
+    for (MUseDefIterator use(candidate); use; use++) {
+        if (!use.def()->isOperandTruncated(use.index()))
+            return false;
+    }
+
+    return true;
+}
+
+static void
+RemoveTruncatesOnOutput(MInstruction *truncated)
+{
+    JS_ASSERT(truncated->type() == MIRType_Int32);
+    JS_ASSERT_IF(truncated->range(), truncated->range()->isInt32());
+
+    for (MUseDefIterator use(truncated); use; use++) {
+        MDefinition *def = use.def();
+        if (!def->isTruncateToInt32() || !def->isToInt32())
+            continue;
+
+        def->replaceAllUsesWith(truncated);
+    }
+}
+
+void
+AdjustTruncatedInputs(MInstruction *truncated)
+{
+    MBasicBlock *block = truncated->block();
+    for (size_t i = 0; i < truncated->numOperands(); i++) {
+        if (!truncated->isOperandTruncated(i))
+            continue;
+        if (truncated->getOperand(i)->type() == MIRType_Int32)
+            continue;
+
+        MTruncateToInt32 *op = MTruncateToInt32::New(truncated->getOperand(i));
+        block->insertBefore(truncated, op);
+        truncated->replaceOperand(i, op);
+    }
+
+    if (truncated->isToDouble()) {
+        truncated->replaceAllUsesWith(truncated->getOperand(0));
+        block->discard(truncated);
+    }
+}
+
+// Iterate backward on all instruction and attempt to truncate operations for
+// each instruction which respect the following list of predicates: Has been
+// analyzed by range analysis, the range has no rounding errors, all uses cases
+// are truncating the result.
+//
+// If the truncation of the operation is successful, then the instruction is
+// queue for later updating the graph to restore the type correctness by
+// converting the operands that need to be truncated.
+//
+// We iterate backward because it is likely that a truncated operation truncates
+// some of its operands.
+bool
+RangeAnalysis::truncate()
+{
+    IonSpew(IonSpew_Range, "Do range-base truncation (backward loop)");
+
+    Vector<MInstruction *, 16, SystemAllocPolicy> worklist;
+
+    for (PostorderIterator block(graph_.poBegin()); block != graph_.poEnd(); block++) {
+        for (MInstructionReverseIterator iter(block->rbegin()); iter != block->rend(); iter++) {
+            // Set truncated flag if range analysis ensure that it has no
+            // rounding errors and no freactional part.
+            const Range *r = iter->range();
+            if (!r || r->hasRoundingErrors())
+                continue;
+
+            // Ensure all observable uses are truncated.
+            if (!AllUsesTruncate(*iter))
+                continue;
+
+            // Truncate this instruction if possible.
+            if (!iter->truncate())
+                continue;
+
+            // Delay updates of inputs/outputs to avoid creating node which
+            // would be removed by the truncation of the next operations.
+            iter->setInWorklist();
+            if (!worklist.append(*iter))
+                return false;
+        }
+    }
+
+    // Update inputs/outputs of truncated instructions.
+    IonSpew(IonSpew_Range, "Do graph type fixup (dequeue)");
+    while (!worklist.empty()) {
+        MInstruction *ins = worklist.popCopy();
+        ins->setNotInWorklist();
+        RemoveTruncatesOnOutput(ins);
+        AdjustTruncatedInputs(ins);
+    }
+
+    return true;
+}
--- a/js/src/ion/RangeAnalysis.h
+++ b/js/src/ion/RangeAnalysis.h
@@ -3,16 +3,18 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_range_analysis_h__
 #define jsion_range_analysis_h__
 
+#include "mozilla/FloatingPoint.h"
+
 #include "wtf/Platform.h"
 #include "MIR.h"
 #include "CompileInfo.h"
 #include "IonAnalysis.h"
 
 namespace js {
 namespace ion {
 
@@ -75,27 +77,41 @@ class RangeAnalysis
   protected:
     MIRGraph &graph_;
 
   public:
     RangeAnalysis(MIRGraph &graph);
     bool addBetaNobes();
     bool analyze();
     bool removeBetaNobes();
+    bool truncate();
 
   private:
     void analyzeLoop(MBasicBlock *header);
     LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
                                                   MTest *test, BranchDirection direction);
     void analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound, MPhi *phi);
     bool tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins);
     void markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current);
 };
 
 class Range : public TempObject {
+  public:
+    // Int32 are signed, so the value needs 31 bits except for INT_MIN value
+    // which needs 32 bits.
+    static const uint16_t MaxInt32Exponent = 31;
+
+    // Maximal exponenent under which we have no precission loss on double
+    // operations. Double has 52 bits of mantissa, so 2^52+1 cannot be
+    // represented without loss.
+    static const uint16_t MaxTruncatableExponent = MOZ_DOUBLE_EXPONENT_SHIFT;
+
+    // 11 bits of signed exponent, so the max is encoded on 10 bits.
+    static const uint16_t MaxDoubleExponent = MOZ_DOUBLE_EXPONENT_BIAS;
+
   private:
     // Absolute ranges.
     //
     // We represent ranges where the endpoints can be in the set:
     // {-infty} U [INT_MIN, INT_MAX] U {infty}.  A bound of +/-
     // infty means that the value may have overflowed in that
     // direction. When computing the range of an integer
     // instruction, the ranges of the operands can be clamped to
@@ -110,51 +126,81 @@ class Range : public TempObject {
     // be infinite (and could overflow), when using this information to
     // propagate through other ranges, we disregard this fact; if that code
     // executes, then the overflow did not occur, so we may safely assume
     // that the range is [INT_MIN, INT_MAX] instead.
     //
     // To facilitate this trick, we maintain the invariants that:
     // 1) lower_infinite == true implies lower_ == JSVAL_INT_MIN
     // 2) upper_infinite == true implies upper_ == JSVAL_INT_MAX
+    //
+    // As a second and less precise range analysis, we represent the maximal
+    // exponent taken by a value. The exponent is calculated by taking the
+    // absolute value and looking at the position of the highest bit.  All
+    // exponent computation have to be over-estimations of the actual result. On
+    // the Int32 this over approximation is rectified.
+
     int32_t lower_;
     bool lower_infinite_;
+
     int32_t upper_;
     bool upper_infinite_;
 
+    bool decimal_;
+    uint16_t max_exponent_;
+
     // Any symbolic lower or upper bound computed for this term.
     const SymbolicBound *symbolicLower_;
     const SymbolicBound *symbolicUpper_;
 
   public:
     Range()
         : lower_(JSVAL_INT_MIN),
           lower_infinite_(true),
           upper_(JSVAL_INT_MAX),
           upper_infinite_(true),
+          decimal_(true),
+          max_exponent_(MaxDoubleExponent),
           symbolicLower_(NULL),
           symbolicUpper_(NULL)
-    {}
+    {
+        JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
+        JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
+    }
 
-    Range(int64_t l, int64_t h)
-        : symbolicLower_(NULL),
+    Range(int64_t l, int64_t h, bool d = false, uint16_t e = MaxInt32Exponent)
+        : lower_infinite_(true),
+          upper_infinite_(true),
+          decimal_(d),
+          max_exponent_(e),
+          symbolicLower_(NULL),
           symbolicUpper_(NULL)
     {
-        setLower(l);
-        setUpper(h);
+        setLowerInit(l);
+        setUpperInit(h);
+        rectifyExponent();
+        JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
+        JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
     }
 
     Range(const Range &other)
         : lower_(other.lower_),
           lower_infinite_(other.lower_infinite_),
           upper_(other.upper_),
           upper_infinite_(other.upper_infinite_),
+          decimal_(other.decimal_),
+          max_exponent_(other.max_exponent_),
           symbolicLower_(NULL),
           symbolicUpper_(NULL)
-    {}
+    {
+        JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
+        JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
+    }
+
+    Range(const MDefinition *def);
 
     static Range *Truncate(int64_t l, int64_t h);
 
     static int64_t abs64(int64_t x) {
 #ifdef WTF_OS_WINDOWS
         return _abs64(x);
 #else
         return llabs(x);
@@ -168,96 +214,166 @@ class Range : public TempObject {
     }
 
     // Unlike the other operations, unionWith is an in-place
     // modification. This is to avoid a bunch of useless extra
     // copying when chaining together unions when handling Phi
     // nodes.
     void unionWith(const Range *other);
     static Range * intersect(const Range *lhs, const Range *rhs, bool *emptyRange);
-    static Range * addTruncate(const Range *lhs, const Range *rhs);
-    static Range * subTruncate(const Range *lhs, const Range *rhs);
     static Range * add(const Range *lhs, const Range *rhs);
     static Range * sub(const Range *lhs, const Range *rhs);
     static Range * mul(const Range *lhs, const Range *rhs);
     static Range * and_(const Range *lhs, const Range *rhs);
     static Range * shl(const Range *lhs, int32_t c);
     static Range * shr(const Range *lhs, int32_t c);
 
-    static bool precisionLossMul(const Range *lhs, const Range *rhs);
     static bool negativeZeroMul(const Range *lhs, const Range *rhs);
 
     inline void makeLowerInfinite() {
         lower_infinite_ = true;
         lower_ = JSVAL_INT_MIN;
+        if (max_exponent_ < MaxInt32Exponent)
+            max_exponent_ = MaxInt32Exponent;
     }
     inline void makeUpperInfinite() {
         upper_infinite_ = true;
         upper_ = JSVAL_INT_MAX;
+        if (max_exponent_ < MaxInt32Exponent)
+            max_exponent_ = MaxInt32Exponent;
     }
     inline void makeRangeInfinite() {
         makeLowerInfinite();
         makeUpperInfinite();
+        max_exponent_ = MaxDoubleExponent;
     }
 
     inline bool isLowerInfinite() const {
         return lower_infinite_;
     }
     inline bool isUpperInfinite() const {
         return upper_infinite_;
     }
 
-    inline bool isFinite() const {
+    inline bool isInt32() const {
         return !isLowerInfinite() && !isUpperInfinite();
     }
 
+    inline bool hasRoundingErrors() const {
+        return isDecimal() || exponent() >= MaxTruncatableExponent;
+    }
+
+    inline bool isInfinite() const {
+        return exponent() >= MaxDoubleExponent;
+    }
+
+    inline bool isDecimal() const {
+        return decimal_;
+    }
+
+    inline uint16_t exponent() const {
+        return max_exponent_;
+    }
+
+    inline uint16_t numBits() const {
+        return max_exponent_ + 1; // 2^0 -> 1
+    }
+
     inline int32_t lower() const {
         return lower_;
     }
 
     inline int32_t upper() const {
         return upper_;
     }
 
-    inline void setLower(int64_t x) {
+    inline void setLowerInit(int64_t x) {
         if (x > JSVAL_INT_MAX) { // c.c
             lower_ = JSVAL_INT_MAX;
+            lower_infinite_ = false;
         } else if (x < JSVAL_INT_MIN) {
             makeLowerInfinite();
         } else {
             lower_ = (int32_t)x;
             lower_infinite_ = false;
         }
     }
-    inline void setUpper(int64_t x) {
+    inline void setLower(int64_t x) {
+        setLowerInit(x);
+        rectifyExponent();
+        JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
+    }
+    inline void setUpperInit(int64_t x) {
         if (x > JSVAL_INT_MAX) {
             makeUpperInfinite();
         } else if (x < JSVAL_INT_MIN) { // c.c
             upper_ = JSVAL_INT_MIN;
+            upper_infinite_ = false;
         } else {
             upper_ = (int32_t)x;
             upper_infinite_ = false;
         }
     }
-    void set(int64_t l, int64_t h) {
-        setLower(l);
-        setUpper(h);
+    inline void setUpper(int64_t x) {
+        setUpperInit(x);
+        rectifyExponent();
+        JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
+    }
+
+    inline void setInt32() {
+        lower_infinite_ = false;
+        upper_infinite_ = false;
+        decimal_ = false;
+        max_exponent_ = MaxInt32Exponent;
+    }
+
+    inline void set(int64_t l, int64_t h, bool d, uint16_t e) {
+        setLowerInit(l);
+        setUpperInit(h);
+        decimal_ = d;
+        max_exponent_ = e;
+        rectifyExponent();
+        JS_ASSERT_IF(lower_infinite_, lower_ == JSVAL_INT_MIN);
+        JS_ASSERT_IF(upper_infinite_, upper_ == JSVAL_INT_MAX);
+    }
+
+    // Truncate the range to an Int32 range.
+    void truncate();
+
+    // Set the exponent by using the precise range analysis on the full
+    // range of Int32 values. This might shrink the exponent after some
+    // operations.
+    //
+    // Note:
+    //     exponent of JSVAL_INT_MIN == 32
+    //     exponent of JSVAL_INT_MAX == 31
+    inline void rectifyExponent() {
+        if (!isInt32()) {
+            JS_ASSERT(max_exponent_ >= MaxInt32Exponent);
+            return;
+        }
+
+        uint32_t max = Max(abs64((int64_t) lower()), abs64((int64_t) upper()));
+        JS_ASSERT_IF(lower() == JSVAL_INT_MIN, max == (uint32_t) JSVAL_INT_MIN);
+        JS_ASSERT(max <= (uint32_t) JSVAL_INT_MIN);
+        // The number of bits needed to encode |max| is the power of 2 plus one.
+        max_exponent_ = max ? js_FloorLog2wImpl(max) : max;
     }
 
     const SymbolicBound *symbolicLower() const {
         return symbolicLower_;
     }
     const SymbolicBound *symbolicUpper() const {
         return symbolicUpper_;
     }
 
-    void setSymbolicLower(SymbolicBound *bound) {
+    inline void setSymbolicLower(SymbolicBound *bound) {
         symbolicLower_ = bound;
     }
-    void setSymbolicUpper(SymbolicBound *bound) {
+    inline void setSymbolicUpper(SymbolicBound *bound) {
         symbolicUpper_ = bound;
     }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_range_analysis_h__
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -147,22 +147,22 @@ def main(argv):
     if options.tbpl:
         # Running all bits would take forever. Instead, we test a few interesting combinations.
         flags = [
                       ['--no-jm'],
                       ['--ion-eager'],
                       # Below, equivalents the old shell flags: ,m,am,amd,n,mn,amn,amdn,mdn
                       ['--no-ion', '--no-jm', '--no-ti'],
                       ['--no-ion', '--no-ti'],
-                      ['--no-ion', '--no-ti', '-a', '-d'],
+                      ['--no-ion', '--no-ti', '--always-mjit', '--debugjit'],
                       ['--no-ion', '--no-jm'],
                       ['--no-ion'],
-                      ['--no-ion', '-a'],
-                      ['--no-ion', '-a', '-d'],
-                      ['--no-ion', '-d']
+                      ['--no-ion', '--always-mjit'],
+                      ['--no-ion', '--always-mjit', '--debugjit'],
+                      ['--no-ion', '--debugjit']
                     ]
         for test in test_list:
             for variant in flags:
                 new_test = test.copy()
                 new_test.jitflags.extend(variant)
                 job_list.append(new_test)
     elif options.ion:
         flags = [['--no-jm'], ['--ion-eager']]
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/range-analysis.js
@@ -0,0 +1,36 @@
+// |jit-test| no-jm
+// Disable JM until it got investigated in Bug 843902.
+
+// Only fails with Ion.
+function add_xors_1() {
+    var res = 0;
+    var step = 4;
+    for (var i = 0x7fffffff | 0; i >= (1 << step); i -= (i >> step)) {
+        var x = i ^ (i << 1);
+        res += (((x + x) + res + res) | 0);
+    }
+    return res;
+}
+
+var r1 = add_xors_1();
+for (var i = 0; i < 100; i++) {
+    var r2 = add_xors_1();
+    assertEq(r2, r1);
+}
+
+// Only fails with JM
+function add_xors_2() {
+    var res = 0;
+    var step = 4;
+    for (var i = 0x7fffffff | 0; i >= (1 << step); i -= (i >> step)) {
+        var x = i ^ (i << 1);
+        res += ((x + x) + res + res) | 0;
+    }
+    return res;
+}
+
+var r1 = add_xors_2();
+for (var i = 0; i < 100; i++) {
+    var r2 = add_xors_2();
+    assertEq(r1, r2);
+}
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -99,25 +99,27 @@ class Test:
                         test.slow = True
                     elif name == 'allow-oom':
                         test.allow_oom = True
                     elif name == 'valgrind':
                         test.valgrind = options.valgrind
                     elif name == 'tz-pacific':
                         test.tz_pacific = True
                     elif name == 'mjitalways':
-                        test.jitflags.append('-a')
+                        test.jitflags.append('--always-mjit')
                     elif name == 'debug':
-                        test.jitflags.append('-d')
+                        test.jitflags.append('--debugjit')
                     elif name == 'mjit':
-                        test.jitflags.append('-m')
+                        test.jitflags.append('--jm')
+                    elif name == 'no-jm':
+                        test.jitflags.append('--no-jm')
                     elif name == 'ion-eager':
                         test.jitflags.append('--ion-eager')
                     elif name == 'dump-bytecode':
-                        test.jitflags.append('-D')
+                        test.jitflags.append('--dump-bytecode')
                     else:
                         print('warning: unrecognized |jit-test| attribute %s' % part)
 
         if options.valgrind_all:
             test.valgrind = True
 
         return test