Bug 1073928 - IonMonkey: Represent negative zero explicitly in range analysis r=nbp,h4writer
authorDan Gohman <sunfish@mozilla.com>
Tue, 04 Nov 2014 10:26:30 -0800
changeset 230159 f718ec4b4cb013a60fa3ce2941208eb07f456be8
parent 230158 59240f2df2c223d0bf7efbccde8cba6991e7138b
child 230160 2e8c12e04a14bcf717346621dd8177c2c45f842c
push id7326
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:58:42 +0000
treeherdermozilla-aurora@d3a3b2a0f2f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, h4writer
bugs1073928
milestone36.0a1
Bug 1073928 - IonMonkey: Represent negative zero explicitly in range analysis r=nbp,h4writer
js/src/jit-test/tests/ion/bug1073928.js
js/src/jit/CodeGenerator.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/RangeAnalysis.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1073928.js
@@ -0,0 +1,30 @@
+function f(y) {
+    var a = Math.fround(-0);
+    var b = ~Math.hypot(y > 0, 5);
+    assertEq(a, -0);
+    assertEq(b, -6);
+}
+f(-0);
+f(1);
+
+function g(y, z) {
+    if (z == 0) {
+        var a = Math.fround(z);
+        var b = ~Math.hypot(y > 0, 5);
+        assertEq(a, -0);
+        assertEq(b, -6);
+    }
+}
+g(-0, -0);
+g(1, -0);
+
+function h(y, z) {
+    if (z == -0) {
+        var a = Math.fround(z);
+        var b = ~Math.hypot(y > 0, 5);
+        assertEq(a, -0);
+        assertEq(b, -6);
+    }
+}
+h(-0, -0);
+h(1, -0);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -9637,19 +9637,20 @@ CodeGenerator::emitAssertRangeI(const Ra
     // Check the upper bound.
     if (r->hasInt32UpperBound() && r->upper() < INT32_MAX) {
         Label success;
         masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), &success);
         masm.assumeUnreachable("Integer input should be lower or equal than Upperbound.");
         masm.bind(&success);
     }
 
-    // For r->canHaveFractionalPart() and r->exponent(), there's nothing to check, because
-    // if we ended up in the integer range checking code, the value is already
-    // in an integer register in the integer range.
+    // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and
+    // r->exponent(), there's nothing to check, because if we ended up in the
+    // integer range checking code, the value is already in an integer register
+    // in the integer range.
 
     return true;
 }
 
 bool
 CodeGenerator::emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp)
 {
     // Check the lower bound.
@@ -9671,16 +9672,34 @@ CodeGenerator::emitAssertRangeD(const Ra
         masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
         masm.assumeUnreachable("Double input should be lower or equal than Upperbound.");
         masm.bind(&success);
     }
 
     // This code does not yet check r->canHaveFractionalPart(). This would require new
     // assembler interfaces to make rounding instructions available.
 
+    if (!r->canBeNegativeZero()) {
+        Label success;
+
+        // First, test for being equal to 0.0, which also includes -0.0.
+        masm.loadConstantDouble(0.0, temp);
+        masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp, &success);
+
+        // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is
+        // -Infinity instead of Infinity.
+        masm.loadConstantDouble(1.0, temp);
+        masm.divDouble(input, temp);
+        masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success);
+
+        masm.assumeUnreachable("Input shouldn't be negative zero.");
+
+        masm.bind(&success);
+    }
+
     if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
         r->exponent() < FloatingPoint<double>::kExponentBias)
     {
         // Check the bounds implied by the maximum exponent.
         Label exponentLoOk;
         masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
         masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
         masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &exponentLoOk);
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -25,16 +25,17 @@ using namespace js::jit;
 using mozilla::Abs;
 using mozilla::CountLeadingZeroes32;
 using mozilla::NumberEqualsInt32;
 using mozilla::ExponentComponent;
 using mozilla::FloorLog2;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
+using mozilla::IsNegativeZero;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using mozilla::Swap;
 using JS::GenericNaN;
 
 // This algorithm is based on the paper "Eliminating Range Checks Using
 // Static Single Assignment Form" by Gough and Klaren.
 //
@@ -353,24 +354,61 @@ Range::print(Sprinter &sp) const
         sp.printf("%d", upper_);
     if (symbolicUpper_) {
         sp.printf(" {");
         symbolicUpper_->print(sp);
         sp.printf("}");
     }
 
     sp.printf("]");
-    if (IsExponentInteresting(this)) {
-        if (max_exponent_ == IncludesInfinityAndNaN)
-            sp.printf(" (U inf U NaN)", max_exponent_);
-        else if (max_exponent_ == IncludesInfinity)
-            sp.printf(" (U inf)");
-        else
-            sp.printf(" (< pow(2, %d+1))", max_exponent_);
+
+    bool includesNaN = max_exponent_ == IncludesInfinityAndNaN;
+    bool includesNegativeInfinity = max_exponent_ >= IncludesInfinity && !hasInt32LowerBound_;
+    bool includesPositiveInfinity = max_exponent_ >= IncludesInfinity && !hasInt32UpperBound_;
+    bool includesNegativeZero = canBeNegativeZero_;
+
+    if (includesNaN ||
+        includesNegativeInfinity ||
+        includesPositiveInfinity ||
+        includesNegativeZero)
+    {
+        sp.printf(" (");
+        bool first = true;
+        if (includesNaN) {
+            if (first)
+                first = false;
+            else
+                sp.printf(" ");
+            sp.printf("U NaN");
+        }
+        if (includesNegativeInfinity) {
+            if (first)
+                first = false;
+            else
+                sp.printf(" ");
+            sp.printf("U -Infinity");
+        }
+        if (includesPositiveInfinity) {
+            if (first)
+                first = false;
+            else
+                sp.printf(" ");
+            sp.printf("U Infinity");
+        }
+        if (includesNegativeZero) {
+            if (first)
+                first = false;
+            else
+                sp.printf(" ");
+            sp.printf("U -0");
+        }
+        sp.printf(")");
     }
+    if (max_exponent_ < IncludesInfinity && IsExponentInteresting(this))
+        sp.printf(" (< pow(2, %d+1))", max_exponent_);
 }
 
 void
 Range::dump(FILE *fp) const
 {
     Sprinter sp(GetIonContext()->cx);
     sp.init();
     print(sp);
@@ -412,17 +450,22 @@ Range::intersect(TempAllocator &alloc, c
         // If both ranges can be NaN, the result can still be NaN.
         if (!lhs->canBeNaN() || !rhs->canBeNaN())
             *emptyRange = true;
         return nullptr;
     }
 
     bool newHasInt32LowerBound = lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_;
     bool newHasInt32UpperBound = lhs->hasInt32UpperBound_ || rhs->hasInt32UpperBound_;
-    bool newFractional = lhs->canHaveFractionalPart_ && rhs->canHaveFractionalPart_;
+
+    FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(lhs->canHaveFractionalPart_ &&
+                                                                     rhs->canHaveFractionalPart_);
+    NegativeZeroFlag newMayIncludeNegativeZero = NegativeZeroFlag(lhs->canBeNegativeZero_ &&
+                                                                  rhs->canBeNegativeZero_);
+
     uint16_t newExponent = Min(lhs->max_exponent_, rhs->max_exponent_);
 
     // NaN is a special value which is neither greater than infinity or less than
     // negative infinity. When we intersect two ranges like [?, 0] and [0, ?], we
     // can end up thinking we have both a lower and upper bound, even though NaN
     // is still possible. In this case, just be conservative, since any case where
     // we can have NaN is not especially interesting.
     if (newHasInt32LowerBound && newHasInt32UpperBound && newExponent == IncludesInfinityAndNaN)
@@ -440,18 +483,18 @@ Range::intersect(TempAllocator &alloc, c
     //
     // When intersecting such a range with an integer range, the fractional part
     // of the range is dropped. The max exponent of 0 remains valid, so the
     // upper bound needs to be adjusted to 1.
     //
     // When intersecting F[0,2] (< pow(2, 0+1)) with a range like F[2,4],
     // the naive intersection is I[2,2], but since the max exponent tells us
     // that the value is always less than 2, the intersection is actually empty.
-    if (lhs->canHaveFractionalPart_ != rhs->canHaveFractionalPart_ ||
-        (lhs->canHaveFractionalPart_ &&
+    if (lhs->canHaveFractionalPart() != rhs->canHaveFractionalPart() ||
+        (lhs->canHaveFractionalPart() &&
          newHasInt32LowerBound && newHasInt32UpperBound &&
          newLower == newUpper))
     {
         refineInt32BoundsByExponent(newExponent,
                                     &newLower, &newHasInt32LowerBound,
                                     &newUpper, &newHasInt32UpperBound);
 
         // If we're intersecting two ranges that don't overlap, this could also
@@ -459,32 +502,42 @@ Range::intersect(TempAllocator &alloc, c
         // the empty set.
         if (newLower > newUpper) {
             *emptyRange = true;
             return nullptr;
         }
     }
 
     return new(alloc) Range(newLower, newHasInt32LowerBound, newUpper, newHasInt32UpperBound,
-                            newFractional, newExponent);
+                            newCanHaveFractionalPart,
+                            newMayIncludeNegativeZero,
+                            newExponent);
 }
 
 void
 Range::unionWith(const Range *other)
 {
     int32_t newLower = Min(lower_, other->lower_);
     int32_t newUpper = Max(upper_, other->upper_);
 
     bool newHasInt32LowerBound = hasInt32LowerBound_ && other->hasInt32LowerBound_;
     bool newHasInt32UpperBound = hasInt32UpperBound_ && other->hasInt32UpperBound_;
-    bool newFractional = canHaveFractionalPart_ || other->canHaveFractionalPart_;
+
+    FractionalPartFlag newCanHaveFractionalPart =
+        FractionalPartFlag(canHaveFractionalPart_ ||
+                           other->canHaveFractionalPart_);
+    NegativeZeroFlag newMayIncludeNegativeZero = NegativeZeroFlag(canBeNegativeZero_ ||
+                                                                  other->canBeNegativeZero_);
+
     uint16_t newExponent = Max(max_exponent_, other->max_exponent_);
 
     rawInitialize(newLower, newHasInt32LowerBound, newUpper, newHasInt32UpperBound,
-                  newFractional, newExponent);
+                  newCanHaveFractionalPart,
+                  newMayIncludeNegativeZero,
+                  newExponent);
 }
 
 Range::Range(const MDefinition *def)
   : symbolicLower_(nullptr),
     symbolicUpper_(nullptr)
 {
     if (const Range *other = def->range()) {
         // The instruction has range information; use it.
@@ -545,16 +598,18 @@ ExponentImpliedByDouble(double d)
     // Otherwise take the exponent part and clamp it at zero, since the Range
     // class doesn't track fractional ranges.
     return uint16_t(Max(int_fast16_t(0), ExponentComponent(d)));
 }
 
 void
 Range::setDouble(double l, double h)
 {
+    MOZ_ASSERT(!(l > h));
+
     // Infer lower_, upper_, hasInt32LowerBound_, and hasInt32UpperBound_.
     if (l >= INT32_MIN && l <= INT32_MAX) {
         lower_ = int32_t(::floor(l));
         hasInt32LowerBound_ = true;
     } else {
         lower_ = INT32_MIN;
         hasInt32LowerBound_ = false;
     }
@@ -566,29 +621,52 @@ Range::setDouble(double l, double h)
         hasInt32UpperBound_ = false;
     }
 
     // Infer max_exponent_.
     uint16_t lExp = ExponentImpliedByDouble(l);
     uint16_t hExp = ExponentImpliedByDouble(h);
     max_exponent_ = Max(lExp, hExp);
 
-    // Infer the canHaveFractionalPart_ field. We can have a fractional part
-    // if the range crosses through the neighborhood of zero. We won't have a
-    // fractional value if the value is always beyond the point at which
-    // double precision can't represent fractional values.
+    canHaveFractionalPart_ = ExcludesFractionalParts;
+    canBeNegativeZero_ = ExcludesNegativeZero;
+
+    // Infer the canHaveFractionalPart_ setting. We can have a
+    // fractional part if the range crosses through the neighborhood of zero. We
+    // won't have a fractional value if the value is always beyond the point at
+    // which double precision can't represent fractional values.
     uint16_t minExp = Min(lExp, hExp);
     bool includesNegative = IsNaN(l) || l < 0;
     bool includesPositive = IsNaN(h) || h > 0;
     bool crossesZero = includesNegative && includesPositive;
-    canHaveFractionalPart_ = crossesZero || minExp < MaxTruncatableExponent;
+    if (crossesZero || minExp < MaxTruncatableExponent)
+        canHaveFractionalPart_ = IncludesFractionalParts;
+
+    // Infer the canBeNegativeZero_ setting. We can have a negative zero if
+    // either bound is zero.
+    if (!(l > 0) && !(h < 0))
+        canBeNegativeZero_ = IncludesNegativeZero;
 
     optimize();
 }
 
+void
+Range::setDoubleSingleton(double d)
+{
+    setDouble(d, d);
+
+    // The above setDouble call is for comparisons, and treats negative zero
+    // as equal to zero. We're aiming for a minimum range, so we can clear the
+    // negative zero flag if the value isn't actually negative zero.
+    if (!IsNegativeZero(d))
+        canBeNegativeZero_ = ExcludesNegativeZero;
+
+    assertInvariants();
+}
+
 static inline bool
 MissingAnyInt32Bounds(const Range *lhs, const Range *rhs)
 {
     return !lhs->hasInt32Bounds() || !rhs->hasInt32Bounds();
 }
 
 Range *
 Range::add(TempAllocator &alloc, const Range *lhs, const Range *rhs)
@@ -606,17 +684,22 @@ Range::add(TempAllocator &alloc, const R
     uint16_t e = Max(lhs->max_exponent_, rhs->max_exponent_);
     if (e <= Range::MaxFiniteExponent)
         ++e;
 
     // Infinity + -Infinity is NaN.
     if (lhs->canBeInfiniteOrNaN() && rhs->canBeInfiniteOrNaN())
         e = Range::IncludesInfinityAndNaN;
 
-    return new(alloc) Range(l, h, lhs->canHaveFractionalPart() || rhs->canHaveFractionalPart(), e);
+    return new(alloc) Range(l, h,
+                            FractionalPartFlag(lhs->canHaveFractionalPart() ||
+                                               rhs->canHaveFractionalPart()),
+                            NegativeZeroFlag(lhs->canBeNegativeZero() &&
+                                             rhs->canBeNegativeZero()),
+                            e);
 }
 
 Range *
 Range::sub(TempAllocator &alloc, const Range *lhs, const Range *rhs)
 {
     int64_t l = (int64_t) lhs->lower_ - (int64_t) rhs->upper_;
     if (!lhs->hasInt32LowerBound() || !rhs->hasInt32UpperBound())
         l = NoInt32LowerBound;
@@ -630,17 +713,22 @@ Range::sub(TempAllocator &alloc, const R
     uint16_t e = Max(lhs->max_exponent_, rhs->max_exponent_);
     if (e <= Range::MaxFiniteExponent)
         ++e;
 
     // Infinity - Infinity is NaN.
     if (lhs->canBeInfiniteOrNaN() && rhs->canBeInfiniteOrNaN())
         e = Range::IncludesInfinityAndNaN;
 
-    return new(alloc) Range(l, h, lhs->canHaveFractionalPart() || rhs->canHaveFractionalPart(), e);
+    return new(alloc) Range(l, h,
+                            FractionalPartFlag(lhs->canHaveFractionalPart() ||
+                                               rhs->canHaveFractionalPart()),
+                            NegativeZeroFlag(lhs->canBeNegativeZero() &&
+                                             rhs->canBeZero()),
+                            e);
 }
 
 Range *
 Range::and_(TempAllocator &alloc, const Range *lhs, const Range *rhs)
 {
     MOZ_ASSERT(lhs->isInt32());
     MOZ_ASSERT(rhs->isInt32());
 
@@ -791,17 +879,22 @@ Range::not_(TempAllocator &alloc, const 
 {
     MOZ_ASSERT(op->isInt32());
     return Range::NewInt32Range(alloc, ~op->upper(), ~op->lower());
 }
 
 Range *
 Range::mul(TempAllocator &alloc, const Range *lhs, const Range *rhs)
 {
-    bool fractional = lhs->canHaveFractionalPart() || rhs->canHaveFractionalPart();
+    FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(lhs->canHaveFractionalPart_ ||
+                                                                     rhs->canHaveFractionalPart_);
+
+    NegativeZeroFlag newMayIncludeNegativeZero =
+        NegativeZeroFlag((lhs->canHaveSignBitSet() && rhs->canBeFiniteNonNegative()) ||
+                         (rhs->canHaveSignBitSet() && lhs->canBeFiniteNonNegative()));
 
     uint16_t exponent;
     if (!lhs->canBeInfiniteOrNaN() && !rhs->canBeInfiniteOrNaN()) {
         // Two finite values.
         exponent = lhs->numBits() + rhs->numBits() - 1;
         if (exponent > Range::MaxFiniteExponent)
             exponent = Range::IncludesInfinity;
     } else if (!lhs->canBeNaN() &&
@@ -812,25 +905,30 @@ Range::mul(TempAllocator &alloc, const R
         // Two values that multiplied together won't produce a NaN.
         exponent = Range::IncludesInfinity;
     } else {
         // Could be anything.
         exponent = Range::IncludesInfinityAndNaN;
     }
 
     if (MissingAnyInt32Bounds(lhs, rhs))
-        return new(alloc) Range(NoInt32LowerBound, NoInt32UpperBound, fractional, exponent);
+        return new(alloc) Range(NoInt32LowerBound, NoInt32UpperBound,
+                                newCanHaveFractionalPart,
+                                newMayIncludeNegativeZero,
+                                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(alloc) Range(
         Min( Min(a, b), Min(c, d) ),
         Max( Max(a, b), Max(c, d) ),
-        fractional, exponent);
+        newCanHaveFractionalPart,
+        newMayIncludeNegativeZero,
+        exponent);
 }
 
 Range *
 Range::lsh(TempAllocator &alloc, const Range *lhs, int32_t c)
 {
     MOZ_ASSERT(lhs->isInt32());
     int32_t shift = c & 0x1f;
 
@@ -906,52 +1004,69 @@ Range::ursh(TempAllocator &alloc, const 
     return Range::NewUInt32Range(alloc, 0, lhs->isFiniteNonNegative() ? lhs->upper() : UINT32_MAX);
 }
 
 Range *
 Range::abs(TempAllocator &alloc, const Range *op)
 {
     int32_t l = op->lower_;
     int32_t u = op->upper_;
+    FractionalPartFlag canHaveFractionalPart = op->canHaveFractionalPart_;
+
+    // Abs never produces a negative zero.
+    NegativeZeroFlag canBeNegativeZero = ExcludesNegativeZero;
 
     return new(alloc) Range(Max(Max(int32_t(0), l), u == INT32_MIN ? INT32_MAX : -u),
                             true,
                             Max(Max(int32_t(0), u), l == INT32_MIN ? INT32_MAX : -l),
                             op->hasInt32Bounds() && l != INT32_MIN,
-                            op->canHaveFractionalPart_,
+                            canHaveFractionalPart,
+                            canBeNegativeZero,
                             op->max_exponent_);
 }
 
 Range *
 Range::min(TempAllocator &alloc, const Range *lhs, const Range *rhs)
 {
     // If either operand is NaN, the result is NaN.
     if (lhs->canBeNaN() || rhs->canBeNaN())
         return nullptr;
 
+    FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(lhs->canHaveFractionalPart_ ||
+                                                                     rhs->canHaveFractionalPart_);
+    NegativeZeroFlag newMayIncludeNegativeZero = NegativeZeroFlag(lhs->canBeNegativeZero_ ||
+                                                                  rhs->canBeNegativeZero_);
+
     return new(alloc) Range(Min(lhs->lower_, rhs->lower_),
                             lhs->hasInt32LowerBound_ && rhs->hasInt32LowerBound_,
                             Min(lhs->upper_, rhs->upper_),
                             lhs->hasInt32UpperBound_ || rhs->hasInt32UpperBound_,
-                            lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_,
+                            newCanHaveFractionalPart,
+                            newMayIncludeNegativeZero,
                             Max(lhs->max_exponent_, rhs->max_exponent_));
 }
 
 Range *
 Range::max(TempAllocator &alloc, const Range *lhs, const Range *rhs)
 {
     // If either operand is NaN, the result is NaN.
     if (lhs->canBeNaN() || rhs->canBeNaN())
         return nullptr;
 
+    FractionalPartFlag newCanHaveFractionalPart = FractionalPartFlag(lhs->canHaveFractionalPart_ ||
+                                                                     rhs->canHaveFractionalPart_);
+    NegativeZeroFlag newMayIncludeNegativeZero = NegativeZeroFlag(lhs->canBeNegativeZero_ ||
+                                                                  rhs->canBeNegativeZero_);
+
     return new(alloc) Range(Max(lhs->lower_, rhs->lower_),
                             lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_,
                             Max(lhs->upper_, rhs->upper_),
                             lhs->hasInt32UpperBound_ && rhs->hasInt32UpperBound_,
-                            lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_,
+                            newCanHaveFractionalPart,
+                            newMayIncludeNegativeZero,
                             Max(lhs->max_exponent_, rhs->max_exponent_));
 }
 
 Range *
 Range::floor(TempAllocator &alloc, const Range *op)
 {
     Range *copy = new(alloc) Range(*op);
     // Decrement lower bound of copy range if op have a factional part and lower
@@ -965,17 +1080,17 @@ Range::floor(TempAllocator &alloc, const
     // But, if we don't have those, value's max_exponent_ may have changed.
     // Because we're looking to maintain an over estimation, if we can,
     // we increment it.
     if(copy->hasInt32Bounds())
         copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
     else if(copy->max_exponent_ < MaxFiniteExponent)
         copy->max_exponent_++;
 
-    copy->canHaveFractionalPart_ = false;
+    copy->canHaveFractionalPart_ = ExcludesFractionalParts;
     copy->assertInvariants();
     return copy;
 }
 
 Range *
 Range::ceil(TempAllocator &alloc, const Range *op)
 {
     Range *copy = new(alloc) Range(*op);
@@ -984,46 +1099,48 @@ Range::ceil(TempAllocator &alloc, const 
     // If we have got int32 bounds defined, just deduce it using the defined bounds.
     // Else we can just increment its value,
     // as we are looking to maintain an over estimation.
     if (copy->hasInt32Bounds())
         copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
     else if (copy->max_exponent_ < MaxFiniteExponent)
         copy->max_exponent_++;
 
-    copy->canHaveFractionalPart_ = false;
+    copy->canHaveFractionalPart_ = ExcludesFractionalParts;
     copy->assertInvariants();
     return copy;
 }
 
 bool
 Range::negativeZeroMul(const Range *lhs, const Range *rhs)
 {
     // The result can only be negative zero if both sides are finite and they
     // have differing signs.
-    return (lhs->canBeFiniteNegative() && rhs->canBeFiniteNonNegative()) ||
-           (rhs->canBeFiniteNegative() && lhs->canBeFiniteNonNegative());
+    return (lhs->canHaveSignBitSet() && rhs->canBeFiniteNonNegative()) ||
+           (rhs->canHaveSignBitSet() && lhs->canBeFiniteNonNegative());
 }
 
 bool
 Range::update(const Range *other)
 {
     bool changed =
         lower_ != other->lower_ ||
         hasInt32LowerBound_ != other->hasInt32LowerBound_ ||
         upper_ != other->upper_ ||
         hasInt32UpperBound_ != other->hasInt32UpperBound_ ||
         canHaveFractionalPart_ != other->canHaveFractionalPart_ ||
+        canBeNegativeZero_ != other->canBeNegativeZero_ ||
         max_exponent_ != other->max_exponent_;
     if (changed) {
         lower_ = other->lower_;
         hasInt32LowerBound_ = other->hasInt32LowerBound_;
         upper_ = other->upper_;
         hasInt32UpperBound_ = other->hasInt32UpperBound_;
         canHaveFractionalPart_ = other->canHaveFractionalPart_;
+        canBeNegativeZero_ = other->canBeNegativeZero_;
         max_exponent_ = other->max_exponent_;
         assertInvariants();
     }
 
     return changed;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1074,17 +1191,17 @@ MBeta::computeRange(TempAllocator &alloc
     }
 }
 
 void
 MConstant::computeRange(TempAllocator &alloc)
 {
     if (value().isNumber()) {
         double d = value().toNumber();
-        setRange(Range::NewDoubleRange(alloc, d, d));
+        setRange(Range::NewDoubleSingletonRange(alloc, d));
     } else if (value().isBoolean()) {
         bool b = value().toBoolean();
         setRange(Range::NewInt32Range(alloc, b, b));
     }
 }
 
 void
 MCharCodeAt::computeRange(TempAllocator &alloc)
@@ -1277,16 +1394,18 @@ MMul::computeRange(TempAllocator &alloc)
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
     Range left(getOperand(0));
     Range right(getOperand(1));
     if (canBeNegativeZero())
         canBeNegativeZero_ = Range::negativeZeroMul(&left, &right);
     Range *next = Range::mul(alloc, &left, &right);
+    if (!next->canBeNegativeZero())
+        canBeNegativeZero_ = false;
     // Truncated multiplications could overflow in both directions
     if (isTruncated())
         next->wrapAroundToInt32();
     setRange(next);
 }
 
 void
 MMod::computeRange(TempAllocator &alloc)
@@ -1363,17 +1482,28 @@ MMod::computeRange(TempAllocator &alloc)
     int64_t absBound = Min(lhsAbsBound, rhsAbsBound);
 
     // Now consider the sign of the result.
     // If lhs is non-negative, the result will be non-negative.
     // If lhs is non-positive, the result will be non-positive.
     int64_t lower = lhs.lower() >= 0 ? 0 : -absBound;
     int64_t upper = lhs.upper() <= 0 ? 0 : absBound;
 
-    setRange(new(alloc) Range(lower, upper, lhs.canHaveFractionalPart() || rhs.canHaveFractionalPart(),
+    Range::FractionalPartFlag newCanHaveFractionalPart =
+        Range::FractionalPartFlag(lhs.canHaveFractionalPart() ||
+                                  rhs.canHaveFractionalPart());
+
+    // If the lhs can have the sign bit set and we can return a zero, it'll be a
+    // negative zero.
+    Range::NegativeZeroFlag newMayIncludeNegativeZero =
+        Range::NegativeZeroFlag(lhs.canHaveSignBitSet());
+
+    setRange(new(alloc) Range(lower, upper,
+                              newCanHaveFractionalPart,
+                              newMayIncludeNegativeZero,
                               Min(lhs.exponent(), rhs.exponent())));
 }
 
 void
 MDiv::computeRange(TempAllocator &alloc)
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
@@ -1383,21 +1513,27 @@ MDiv::computeRange(TempAllocator &alloc)
     // If either operand is a NaN, the result is NaN. This also conservatively
     // handles Infinity cases.
     if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds())
         return;
 
     // Something simple for now: When dividing by a positive rhs, the result
     // won't be further from zero than lhs.
     if (lhs.lower() >= 0 && rhs.lower() >= 1) {
-        setRange(new(alloc) Range(0, lhs.upper(), true, lhs.exponent()));
+        setRange(new(alloc) Range(0, lhs.upper(),
+                                  Range::IncludesFractionalParts,
+                                  Range::IncludesNegativeZero,
+                                  lhs.exponent()));
     } else if (unsigned_ && rhs.lower() >= 1) {
         // We shouldn't set the unsigned flag if the inputs can have
         // fractional parts.
         MOZ_ASSERT(!lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart());
+        // We shouldn't set the unsigned flag if the inputs can be
+        // negative zero.
+        MOZ_ASSERT(!lhs.canBeNegativeZero() && !rhs.canBeNegativeZero());
         // Unsigned division by a non-zero rhs will return a uint32 value.
         setRange(Range::NewUInt32Range(alloc, 0, UINT32_MAX));
     }
 }
 
 void
 MSqrt::computeRange(TempAllocator &alloc)
 {
@@ -1409,17 +1545,21 @@ MSqrt::computeRange(TempAllocator &alloc
         return;
 
     // Sqrt of a negative non-zero value is NaN.
     if (input.lower() < 0)
         return;
 
     // Something simple for now: When taking the sqrt of a positive value, the
     // result won't be further from zero than the input.
-    setRange(new(alloc) Range(0, input.upper(), true, input.exponent()));
+    // And, sqrt of an integer may have a fractional part.
+    setRange(new(alloc) Range(0, input.upper(),
+                              Range::IncludesFractionalParts,
+                              input.canBeNegativeZero(),
+                              input.exponent()));
 }
 
 void
 MToDouble::computeRange(TempAllocator &alloc)
 {
     setRange(new(alloc) Range(getOperand(0)));
 }
 
@@ -1575,17 +1715,22 @@ MMathFunction::computeRange(TempAllocato
     default:
         break;
     }
 }
 
 void
 MRandom::computeRange(TempAllocator &alloc)
 {
-    setRange(Range::NewDoubleRange(alloc, 0.0, 1.0));
+    Range *r = Range::NewDoubleRange(alloc, 0.0, 1.0);
+
+    // Random never returns negative zero.
+    r->refineToExcludeNegativeZero();
+
+    setRange(r);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Range Analysis
 ///////////////////////////////////////////////////////////////////////////////
 
 bool
 RangeAnalysis::analyzeLoop(MBasicBlock *header)
@@ -2120,42 +2265,47 @@ Range::clampToInt32()
 }
 
 void
 Range::wrapAroundToInt32()
 {
     if (!hasInt32Bounds()) {
         setInt32(JSVAL_INT_MIN, JSVAL_INT_MAX);
     } else if (canHaveFractionalPart()) {
-        canHaveFractionalPart_ = false;
-
         // Clearing the fractional field may provide an opportunity to refine
         // lower_ or upper_.
+        canHaveFractionalPart_ = ExcludesFractionalParts;
+        canBeNegativeZero_ = ExcludesNegativeZero;
         refineInt32BoundsByExponent(max_exponent_,
                                     &lower_, &hasInt32LowerBound_,
                                     &upper_, &hasInt32UpperBound_);
 
         assertInvariants();
+    } else {
+        // If nothing else, we can clear the negative zero flag.
+        canBeNegativeZero_ = ExcludesNegativeZero;
     }
+    MOZ_ASSERT(isInt32());
 }
 
 void
 Range::wrapAroundToShiftCount()
 {
     wrapAroundToInt32();
     if (lower() < 0 || upper() >= 32)
         setInt32(0, 31);
 }
 
 void
 Range::wrapAroundToBoolean()
 {
     wrapAroundToInt32();
     if (!isBoolean())
         setInt32(0, 1);
+    MOZ_ASSERT(isBoolean());
 }
 
 bool
 MDefinition::needTruncation(TruncateKind kind)
 {
     // No procedure defined for truncating this instruction.
     return false;
 }
@@ -2941,17 +3091,17 @@ MMod::collectRangeInfoPreTrunc()
         canBeDivideByZero_ = false;
 
 }
 
 void
 MToInt32::collectRangeInfoPreTrunc()
 {
     Range inputRange(input());
-    if (!inputRange.canBeZero())
+    if (!inputRange.canBeNegativeZero())
         canBeNegativeZero_ = false;
 }
 
 void
 MBoundsCheckLower::collectRangeInfoPreTrunc()
 {
     Range indexRange(index());
     if (indexRange.hasInt32LowerBound() && indexRange.lower() >= minimum_)
@@ -2973,17 +3123,17 @@ MNot::collectRangeInfoPreTrunc()
 }
 
 void
 MPowHalf::collectRangeInfoPreTrunc()
 {
     Range inputRange(input());
     if (!inputRange.canBeInfiniteOrNaN() || inputRange.hasInt32LowerBound())
         operandIsNeverNegativeInfinity_ = true;
-    if (!inputRange.canBeZero())
+    if (!inputRange.canBeNegativeZero())
         operandIsNeverNegativeZero_ = true;
     if (!inputRange.canBeNaN())
         operandIsNeverNaN_ = true;
 }
 
 void
 MUrsh::collectRangeInfoPreTrunc()
 {
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -149,16 +149,25 @@ class Range : public TempObject {
     // use int64_t, which either holds an int32_t value, or one of the following
     // special values which mean a value which is beyond the int32 range,
     // potentially including infinity or NaN. These special values are
     // guaranteed to compare greater, and less than, respectively, any int32_t
     // value.
     static const int64_t NoInt32UpperBound = int64_t(JSVAL_INT_MAX) + 1;
     static const int64_t NoInt32LowerBound = int64_t(JSVAL_INT_MIN) - 1;
 
+    enum FractionalPartFlag {
+        ExcludesFractionalParts = false,
+        IncludesFractionalParts = true
+    };
+    enum NegativeZeroFlag {
+        ExcludesNegativeZero = false,
+        IncludesNegativeZero = true
+    };
+
   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
@@ -181,22 +190,23 @@ class Range : public TempObject {
     //
     // 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 hasInt32LowerBound_;
+    int32_t upper_;
 
-    int32_t upper_;
+    bool hasInt32LowerBound_;
     bool hasInt32UpperBound_;
 
-    bool canHaveFractionalPart_;
+    FractionalPartFlag canHaveFractionalPart_ : 1;
+    NegativeZeroFlag canBeNegativeZero_ : 1;
     uint16_t max_exponent_;
 
     // Any symbolic lower or upper bound computed for this term.
     const SymbolicBound *symbolicLower_;
     const SymbolicBound *symbolicUpper_;
 
     // This function simply makes several MOZ_ASSERTs to verify the internal
     // consistency of this range.
@@ -212,28 +222,28 @@ class Range : public TempObject {
 
         // max_exponent_ must be one of three possible things.
         MOZ_ASSERT(max_exponent_ <= MaxFiniteExponent ||
                    max_exponent_ == IncludesInfinity ||
                    max_exponent_ == IncludesInfinityAndNaN);
 
         // Forbid the max_exponent_ field from implying better bounds for
         // lower_/upper_ fields. We have to add 1 to the max_exponent_ when
-        // canHaveFractionalPart_ is true in order to accomodate fractional
-        // offsets. For example, 2147483647.9 is greater than INT32_MAX, so a
-        // range containing that value will have hasInt32UpperBound_ set to
-        // false, however that value also has exponent 30, which is strictly
-        // less than MaxInt32Exponent. For another example, 1.9 has an exponent
-        // of 0 but requires upper_ to be at least 2, which has exponent 1.
+        // canHaveFractionalPart_ is true in order to accomodate
+        // fractional offsets. For example, 2147483647.9 is greater than
+        // INT32_MAX, so a range containing that value will have
+        // hasInt32UpperBound_ set to false, however that value also has
+        // exponent 30, which is strictly less than MaxInt32Exponent. For
+        // another example, 1.9 has an exponent of 0 but requires upper_ to be
+        // at least 2, which has exponent 1.
+        uint32_t adjustedExponent = max_exponent_ + (canHaveFractionalPart_ ? 1 : 0);
         MOZ_ASSERT_IF(!hasInt32LowerBound_ || !hasInt32UpperBound_,
-                      max_exponent_ + canHaveFractionalPart_ >= MaxInt32Exponent);
-        MOZ_ASSERT(max_exponent_ + canHaveFractionalPart_ >=
-                   mozilla::FloorLog2(mozilla::Abs(upper_)));
-        MOZ_ASSERT(max_exponent_ + canHaveFractionalPart_ >=
-                   mozilla::FloorLog2(mozilla::Abs(lower_)));
+                      adjustedExponent >= MaxInt32Exponent);
+        MOZ_ASSERT(adjustedExponent >= mozilla::FloorLog2(mozilla::Abs(upper_)));
+        MOZ_ASSERT(adjustedExponent >= mozilla::FloorLog2(mozilla::Abs(lower_)));
 
         // The following are essentially static assertions, but FloorLog2 isn't
         // trivially suitable for constexpr :(.
         MOZ_ASSERT(mozilla::FloorLog2(JSVAL_INT_MIN) == MaxInt32Exponent);
         MOZ_ASSERT(mozilla::FloorLog2(JSVAL_INT_MAX) == 30);
         MOZ_ASSERT(mozilla::FloorLog2(UINT32_MAX) == MaxUInt32Exponent);
         MOZ_ASSERT(mozilla::FloorLog2(0) == 0);
     }
@@ -316,93 +326,127 @@ class Range : public TempObject {
             if (newExponent < max_exponent_) {
                 max_exponent_ = newExponent;
                 assertInvariants();
             }
 
             // If we have a completely precise range, the value is an integer,
             // since we can only represent integers.
             if (canHaveFractionalPart_ && lower_ == upper_) {
-                canHaveFractionalPart_ = false;
+                canHaveFractionalPart_ = ExcludesFractionalParts;
                 assertInvariants();
             }
         }
+
+        // If the range doesn't include zero, it doesn't include negative zero.
+        if (canBeNegativeZero_ && !canBeZero()) {
+            canBeNegativeZero_ = ExcludesNegativeZero;
+            assertInvariants();
+        }
     }
 
     // Set the range fields to the given raw values.
-    void rawInitialize(int32_t l, bool lb, int32_t h, bool hb, bool f, uint16_t e) {
+    void rawInitialize(int32_t l, bool lb, int32_t h, bool hb,
+                       FractionalPartFlag canHaveFractionalPart,
+                       NegativeZeroFlag canBeNegativeZero,
+                       uint16_t e)
+    {
         lower_ = l;
+        upper_ = h;
         hasInt32LowerBound_ = lb;
-        upper_ = h;
         hasInt32UpperBound_ = hb;
-        canHaveFractionalPart_ = f;
+        canHaveFractionalPart_ = canHaveFractionalPart;
+        canBeNegativeZero_ = canBeNegativeZero;
         max_exponent_ = e;
         optimize();
     }
 
     // Construct a range from the given raw values.
-    Range(int32_t l, bool lb, int32_t h, bool hb, bool f, uint16_t e)
+    Range(int32_t l, bool lb, int32_t h, bool hb,
+          FractionalPartFlag canHaveFractionalPart,
+          NegativeZeroFlag canBeNegativeZero,
+          uint16_t e)
       : symbolicLower_(nullptr),
         symbolicUpper_(nullptr)
      {
-        rawInitialize(l, lb, h, hb, f, e);
+        rawInitialize(l, lb, h, hb, canHaveFractionalPart, canBeNegativeZero, e);
      }
 
   public:
     Range()
       : symbolicLower_(nullptr),
         symbolicUpper_(nullptr)
     {
         setUnknown();
     }
 
-    Range(int64_t l, int64_t h, bool f = false, uint16_t e = MaxInt32Exponent)
+    Range(int64_t l, int64_t h,
+          FractionalPartFlag canHaveFractionalPart,
+          NegativeZeroFlag canBeNegativeZero,
+          uint16_t e)
       : symbolicLower_(nullptr),
         symbolicUpper_(nullptr)
     {
-        set(l, h, f, e);
+        set(l, h, canHaveFractionalPart, canBeNegativeZero, e);
     }
 
     Range(const Range &other)
       : lower_(other.lower_),
+        upper_(other.upper_),
         hasInt32LowerBound_(other.hasInt32LowerBound_),
-        upper_(other.upper_),
         hasInt32UpperBound_(other.hasInt32UpperBound_),
         canHaveFractionalPart_(other.canHaveFractionalPart_),
+        canBeNegativeZero_(other.canBeNegativeZero_),
         max_exponent_(other.max_exponent_),
         symbolicLower_(nullptr),
         symbolicUpper_(nullptr)
     {
         assertInvariants();
     }
 
     // Construct a range from the given MDefinition. This differs from the
     // MDefinition's range() method in that it describes the range of values
     // *after* any bailout checks.
     explicit Range(const MDefinition *def);
 
     static Range *NewInt32Range(TempAllocator &alloc, int32_t l, int32_t h) {
-        return new(alloc) Range(l, h, false, MaxInt32Exponent);
+        return new(alloc) Range(l, h, ExcludesFractionalParts, ExcludesNegativeZero, MaxInt32Exponent);
     }
 
     static Range *NewUInt32Range(TempAllocator &alloc, uint32_t l, uint32_t h) {
         // For now, just pass them to the constructor as int64_t values.
         // They'll become unbounded if they're not in the int32_t range.
-        return new(alloc) Range(l, h, false, MaxUInt32Exponent);
+        return new(alloc) Range(l, h, ExcludesFractionalParts, ExcludesNegativeZero, MaxUInt32Exponent);
     }
 
+    // Construct a range containing values >= l and <= h. Note that this
+    // function treats negative zero as equal to zero, as >= and <= do. If the
+    // range includes zero, it is assumed to include negative zero too.
     static Range *NewDoubleRange(TempAllocator &alloc, double l, double h) {
         if (mozilla::IsNaN(l) && mozilla::IsNaN(h))
             return nullptr;
 
         Range *r = new(alloc) Range();
         r->setDouble(l, h);
         return r;
     }
 
+    // Construct the strictest possible range containing d, or null if d is NaN.
+    // This function treats negative zero as distinct from zero, since this
+    // makes the strictest possible range containin zero a range which
+    // contains one value rather than two.
+    static Range *NewDoubleSingletonRange(TempAllocator &alloc, double d) {
+        if (mozilla::IsNaN(d))
+            return nullptr;
+
+        Range *r = new(alloc) Range();
+        r->setDoubleSingleton(d);
+        return r;
+    }
+
     void print(Sprinter &sp) const;
     void dump(FILE *fp) const;
     void dump() const;
     bool update(const Range *other);
 
     // 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
@@ -434,16 +478,17 @@ class Range : public TempObject {
     bool isUnknownInt32() const {
         return isInt32() && lower() == INT32_MIN && upper() == INT32_MAX;
     }
 
     bool isUnknown() const {
         return !hasInt32LowerBound_ &&
                !hasInt32UpperBound_ &&
                canHaveFractionalPart_ &&
+               canBeNegativeZero_ &&
                max_exponent_ == IncludesInfinityAndNaN;
     }
 
     bool hasInt32LowerBound() const {
         return hasInt32LowerBound_;
     }
     bool hasInt32UpperBound() const {
         return hasInt32UpperBound_;
@@ -452,52 +497,62 @@ class Range : public TempObject {
     // Test whether the value is known to be within [INT32_MIN,INT32_MAX].
     // Note that this does not necessarily mean the value is an integer.
     bool hasInt32Bounds() const {
         return hasInt32LowerBound() && hasInt32UpperBound();
     }
 
     // Test whether the value is known to be representable as an int32.
     bool isInt32() const {
-        return hasInt32Bounds() && !canHaveFractionalPart();
+        return hasInt32Bounds() &&
+               !canHaveFractionalPart_ &&
+               !canBeNegativeZero_;
     }
 
     // Test whether the given value is known to be either 0 or 1.
     bool isBoolean() const {
-        return lower() >= 0 && upper() <= 1 && !canHaveFractionalPart();
+        return lower() >= 0 && upper() <= 1 &&
+               !canHaveFractionalPart_ &&
+               !canBeNegativeZero_;
     }
 
     bool canHaveRoundingErrors() const {
-        return canHaveFractionalPart() || max_exponent_ >= MaxTruncatableExponent;
+        return canHaveFractionalPart_ ||
+               canBeNegativeZero_ ||
+               max_exponent_ >= MaxTruncatableExponent;
     }
 
     // Test if an integer x belongs to the range.
     bool contains(int32_t x) const {
         return x >= lower_ && x <= upper_;
     }
 
-    // Test whether the range contains zero.
+    // Test whether the range contains zero (of either sign).
     bool canBeZero() const {
         return contains(0);
     }
 
     // Test whether the range contains NaN values.
     bool canBeNaN() const {
         return max_exponent_ == IncludesInfinityAndNaN;
     }
 
     // Test whether the range contains infinities or NaN values.
     bool canBeInfiniteOrNaN() const {
         return max_exponent_ >= IncludesInfinity;
     }
 
-    bool canHaveFractionalPart() const {
+    FractionalPartFlag canHaveFractionalPart() const {
         return canHaveFractionalPart_;
     }
 
+    NegativeZeroFlag canBeNegativeZero() const {
+        return canBeNegativeZero_;
+    }
+
     uint16_t exponent() const {
         MOZ_ASSERT(!canBeInfiniteOrNaN());
         return max_exponent_;
     }
 
     uint16_t numBits() const {
         return exponent() + 1; // 2^0 -> 1
     }
@@ -520,63 +575,94 @@ class Range : public TempObject {
     }
 
     // Test whether all values in this range can are finite and non-negative.
     bool isFiniteNonNegative() const {
         return lower_ >= 0 && !canBeInfiniteOrNaN();
     }
 
     // Test whether a value in this range can possibly be a finite
-    // negative value.
+    // negative value. Note that "negative zero" is not considered negative.
     bool canBeFiniteNegative() const {
         return lower_ < 0;
     }
 
     // Test whether a value in this range can possibly be a finite
     // non-negative value.
     bool canBeFiniteNonNegative() const {
         return upper_ >= 0;
     }
 
+    // Test whether a value in this range can have the sign bit set (not
+    // counting NaN, where the sign bit is meaningless).
+    bool canHaveSignBitSet() const {
+        return !hasInt32LowerBound() || canBeFiniteNegative() || canBeNegativeZero();
+    }
+
     // Set this range to have a lower bound not less than x.
     void refineLower(int32_t x) {
         assertInvariants();
         hasInt32LowerBound_ = true;
         lower_ = Max(lower_, x);
         optimize();
     }
 
     // Set this range to have an upper bound not greater than x.
     void refineUpper(int32_t x) {
         assertInvariants();
         hasInt32UpperBound_ = true;
         upper_ = Min(upper_, x);
         optimize();
     }
 
+    // Set this range to exclude negative zero.
+    void refineToExcludeNegativeZero() {
+        assertInvariants();
+        canBeNegativeZero_ = ExcludesNegativeZero;
+        optimize();
+    }
+
     void setInt32(int32_t l, int32_t h) {
         hasInt32LowerBound_ = true;
         hasInt32UpperBound_ = true;
         lower_ = l;
         upper_ = h;
-        canHaveFractionalPart_ = false;
+        canHaveFractionalPart_ = ExcludesFractionalParts;
+        canBeNegativeZero_ = ExcludesNegativeZero;
         max_exponent_ = exponentImpliedByInt32Bounds();
         assertInvariants();
     }
 
+    // Set this range to include values >= l and <= h. Note that this
+    // function treats negative zero as equal to zero, as >= and <= do. If the
+    // range includes zero, it is assumed to include negative zero too.
     void setDouble(double l, double h);
 
+    // Set this range to the narrowest possible range containing d.
+    // This function treats negative zero as distinct from zero, since this
+    // makes the narrowest possible range containin zero a range which
+    // contains one value rather than two.
+    void setDoubleSingleton(double d);
+
     void setUnknown() {
-        set(NoInt32LowerBound, NoInt32UpperBound, true, IncludesInfinityAndNaN);
+        set(NoInt32LowerBound, NoInt32UpperBound,
+            IncludesFractionalParts,
+            IncludesNegativeZero,
+            IncludesInfinityAndNaN);
         MOZ_ASSERT(isUnknown());
     }
 
-    void set(int64_t l, int64_t h, bool f, uint16_t e) {
+    void set(int64_t l, int64_t h,
+             FractionalPartFlag canHaveFractionalPart,
+             NegativeZeroFlag canBeNegativeZero,
+             uint16_t e)
+    {
         max_exponent_ = e;
-        canHaveFractionalPart_ = f;
+        canHaveFractionalPart_ = canHaveFractionalPart;
+        canBeNegativeZero_ = canBeNegativeZero;
         setLowerInit(l);
         setUpperInit(h);
         optimize();
     }
 
     // Make the lower end of this range at least INT32_MIN, and make
     // the upper end of this range at most INT32_MAX.
     void clampToInt32();