Bug 930708 - IonMonkey: Factor out a DoubleEqualsInt32 from DoubleIsInt32 and use it when negative zero is to be treated as zero. r=nbp
authorDan Gohman <sunfish@google.com>
Sat, 02 Nov 2013 14:29:44 -0700
changeset 167835 592b05772531740002535dcfbe9a38a4b897a547
parent 167834 c7a1b26c690b60fbbcd898be063d4abf1c570fac
child 167836 e9ff16009983c0f79efa0f86f65f11f46c857e44
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs930708
milestone28.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 930708 - IonMonkey: Factor out a DoubleEqualsInt32 from DoubleIsInt32 and use it when negative zero is to be treated as zero. r=nbp
js/src/jit/RangeAnalysis.cpp
js/src/jsmath.cpp
js/src/shell/js.cpp
js/src/vm/Interpreter.cpp
mfbt/FloatingPoint.h
mfbt/tests/TestFloatingPoint.cpp
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -18,17 +18,17 @@
 #include "jit/MIRGraph.h"
 #include "vm/NumericConversions.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Abs;
 using mozilla::CountLeadingZeroes32;
-using mozilla::DoubleIsInt32;
+using mozilla::DoubleEqualsInt32;
 using mozilla::ExponentComponent;
 using mozilla::IsInfinite;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using mozilla::Swap;
@@ -201,29 +201,29 @@ RangeAnalysis::addBetaNodes()
         switch (jsop) {
           case JSOP_LE:
             comp.setDouble(conservativeLower, bound);
             break;
           case JSOP_LT:
             // For integers, if x < c, the upper bound of x is c-1.
             if (val->type() == MIRType_Int32) {
                 int32_t intbound;
-                if (DoubleIsInt32(bound, &intbound) && SafeSub(intbound, 1, &intbound))
+                if (DoubleEqualsInt32(bound, &intbound) && SafeSub(intbound, 1, &intbound))
                     bound = intbound;
             }
             comp.setDouble(conservativeLower, bound);
             break;
           case JSOP_GE:
             comp.setDouble(bound, conservativeUpper);
             break;
           case JSOP_GT:
             // For integers, if x > c, the lower bound of x is c+1.
             if (val->type() == MIRType_Int32) {
                 int32_t intbound;
-                if (DoubleIsInt32(bound, &intbound) && SafeAdd(intbound, 1, &intbound))
+                if (DoubleEqualsInt32(bound, &intbound) && SafeAdd(intbound, 1, &intbound))
                     bound = intbound;
             }
             comp.setDouble(bound, conservativeUpper);
             break;
           case JSOP_EQ:
             comp.setDouble(bound, bound);
             break;
           default:
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -35,16 +35,17 @@
 #include "jstypes.h"
 #include "prmjtime.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::Abs;
+using mozilla::DoubleEqualsInt32;
 using mozilla::DoubleIsInt32;
 using mozilla::ExponentComponent;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::PositiveInfinity;
@@ -606,18 +607,19 @@ js::powi(double x, int y)
 #endif
 double
 js::ecmaPow(double x, double y)
 {
     /*
      * Use powi if the exponent is an integer-valued double. We don't have to
      * check for NaN since a comparison with NaN is always false.
      */
-    if (int32_t(y) == y)
-        return powi(x, int32_t(y));
+    int32_t yi;
+    if (DoubleEqualsInt32(y, &yi))
+        return powi(x, yi);
 
     /*
      * Because C99 and ECMA specify different behavior for pow(),
      * we need to wrap the libm call to make it ECMA compliant.
      */
     if (!IsFinite(y) && (x == 1.0 || x == -1.0))
         return GenericNaN();
     /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -80,16 +80,17 @@
 #else
 # include <libgen.h>
 #endif
 
 using namespace js;
 using namespace js::cli;
 
 using mozilla::ArrayLength;
+using mozilla::DoubleEqualsInt32;
 using mozilla::Maybe;
 using mozilla::PodCopy;
 
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
@@ -592,18 +593,19 @@ Version(JSContext *cx, unsigned argc, js
         args.rval().setInt32(origVersion);
     } else {
         /* Set version. */
         int32_t v = -1;
         if (args[0].isInt32()) {
             v = args[0].toInt32();
         } else if (args[0].isDouble()) {
             double fv = args[0].toDouble();
-            if (int32_t(fv) == fv)
-                v = int32_t(fv);
+            int32_t fvi;
+            if (DoubleEqualsInt32(fv, &fvi))
+                v = fvi;
         }
         if (v < 0 || v > JSVERSION_LATEST) {
             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "version");
             return false;
         }
         JS_SetVersionForCompartment(js::GetContextCompartment(cx), JSVersion(v));
         args.rval().setInt32(origVersion);
     }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -52,16 +52,17 @@
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::DebugOnly;
+using mozilla::DoubleEqualsInt32;
 using mozilla::PodCopy;
 
 /*
  * Note: when Clang 3.2 (32-bit) inlines the two functions below in Interpret,
  * the conservative stack scanner leaks a ton of memory and this negatively
  * influences performance. The JS_NEVER_INLINE is a temporary workaround until
  * we can remove the conservative scanner. See bug 849526 for more info.
  */
@@ -2712,19 +2713,18 @@ CASE(JSOP_TABLESWITCH)
      * default case if the discriminant isn't already an int jsval.  (This
      * opcode is emitted only for dense int-domain switches.)
      */
     const Value &rref = *--REGS.sp;
     int32_t i;
     if (rref.isInt32()) {
         i = rref.toInt32();
     } else {
-        double d;
-        /* Don't use mozilla::DoubleIsInt32; treat -0 (double) as 0. */
-        if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
+        /* Use mozilla::DoubleEqualsInt32 to treat -0 (double) as 0. */
+        if (!rref.isDouble() || !DoubleEqualsInt32(rref.toDouble(), &i))
             ADVANCE_AND_DISPATCH(len);
     }
 
     pc2 += JUMP_OFFSET_LEN;
     int32_t low = GET_JUMP_OFFSET(pc2);
     pc2 += JUMP_OFFSET_LEN;
     int32_t high = GET_JUMP_OFFSET(pc2);
 
--- a/mfbt/FloatingPoint.h
+++ b/mfbt/FloatingPoint.h
@@ -197,25 +197,45 @@ SpecificNaN(int signbit, uint64_t signif
 
 /** Computes the smallest non-zero positive double value. */
 static MOZ_ALWAYS_INLINE double
 MinDoubleValue()
 {
   return BitwiseCast<double>(uint64_t(1));
 }
 
+/**
+ * If d is equal to some int32_t value, set *i to that value and return true;
+ * otherwise return false.
+ *
+ * Note that negative zero is "equal" to zero here. To test whether a value can
+ * be losslessly converted to int32_t and back, use DoubleIsInt32 instead.
+ */
 static MOZ_ALWAYS_INLINE bool
-DoubleIsInt32(double d, int32_t* i)
+DoubleEqualsInt32(double d, int32_t* i)
 {
   /*
    * XXX Casting a double that doesn't truncate to int32_t, to int32_t, induces
    *     undefined behavior.  We should definitely fix this (bug 744965), but as
    *     apparently it "works" in practice, it's not a pressing concern now.
    */
-  return !IsNegativeZero(d) && d == (*i = int32_t(d));
+  return d == (*i = int32_t(d));
+}
+
+/**
+ * If d can be converted to int32_t and back to an identical double value,
+ * set *i to that value and return true; otherwise return false.
+ *
+ * The difference between this and DoubleEqualsInt32 is that this method returns
+ * false for negative zero.
+ */
+static MOZ_ALWAYS_INLINE bool
+DoubleIsInt32(double d, int32_t* i)
+{
+  return !IsNegativeZero(d) && DoubleEqualsInt32(d, i);
 }
 
 /**
  * Computes a NaN value.  Do not use this method if you depend upon a particular
  * NaN value being returned.
  */
 static MOZ_ALWAYS_INLINE double
 UnspecifiedNaN()
--- a/mfbt/tests/TestFloatingPoint.cpp
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -4,16 +4,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/FloatingPoint.h"
 
 #include <math.h>
 
 using mozilla::DoublesAreIdentical;
 using mozilla::DoubleExponentBias;
+using mozilla::DoubleEqualsInt32;
+using mozilla::DoubleIsInt32;
 using mozilla::ExponentComponent;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
@@ -159,16 +161,38 @@ TestPredicates()
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 0xfffffffffff0fULL)));
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 17)));;
   MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 0xfffffffffff0fULL)));
   MOZ_ASSERT(!IsNegativeZero(UnspecifiedNaN()));
   MOZ_ASSERT(IsNegativeZero(-0.0));
   MOZ_ASSERT(!IsNegativeZero(0.0));
   MOZ_ASSERT(!IsNegativeZero(-1.0));
   MOZ_ASSERT(!IsNegativeZero(1.0));
+
+  int32_t i;
+  MOZ_ASSERT(DoubleIsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(!DoubleIsInt32(-0.0, &i));
+  MOZ_ASSERT(DoubleEqualsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(DoubleEqualsInt32(-0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(DoubleIsInt32(INT32_MIN, &i)); MOZ_ASSERT(i == INT32_MIN);
+  MOZ_ASSERT(DoubleIsInt32(INT32_MAX, &i)); MOZ_ASSERT(i == INT32_MAX);
+  MOZ_ASSERT(DoubleEqualsInt32(INT32_MIN, &i)); MOZ_ASSERT(i == INT32_MIN);
+  MOZ_ASSERT(DoubleEqualsInt32(INT32_MAX, &i)); MOZ_ASSERT(i == INT32_MAX);
+  MOZ_ASSERT(!DoubleIsInt32(0.5, &i));
+  MOZ_ASSERT(!DoubleIsInt32(double(INT32_MAX) + 0.1, &i));
+  MOZ_ASSERT(!DoubleIsInt32(double(INT32_MIN) - 0.1, &i));
+  MOZ_ASSERT(!DoubleIsInt32(NegativeInfinity(), &i));
+  MOZ_ASSERT(!DoubleIsInt32(PositiveInfinity(), &i));
+  MOZ_ASSERT(!DoubleIsInt32(UnspecifiedNaN(), &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(0.5, &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(double(INT32_MAX) + 0.1, &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(double(INT32_MIN) - 0.1, &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(NegativeInfinity(), &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(PositiveInfinity(), &i));
+  MOZ_ASSERT(!DoubleEqualsInt32(UnspecifiedNaN(), &i));
 }
 
 int
 main()
 {
   TestDoublesAreIdentical();
   TestExponentComponent();
   TestPredicates();