Bug 899936 - OdinMonkey: fix faulty floating point logic in ExtractNumericLiteral (r=mjrosenb)
authorLuke Wagner <luke@mozilla.com>
Wed, 31 Jul 2013 05:26:29 -0500
changeset 153020 3eef74151413ce2c3d40aa5c8b54632a73243fa2
parent 153019 c5d66af1f8a982e702c32e6b403cb23e255c280d
child 153021 87571852c99ff3ba8d9438a79089829598ad2a42
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjrosenb
bugs899936
milestone25.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 899936 - OdinMonkey: fix faulty floating point logic in ExtractNumericLiteral (r=mjrosenb)
js/src/ion/AsmJS.cpp
js/src/jit-test/tests/asm.js/testLiterals.js
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -31,16 +31,17 @@
 using namespace js;
 using namespace js::frontend;
 using namespace js::ion;
 
 using mozilla::AddToHash;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::HashGeneric;
+using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::MoveRef;
 
 static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 /*****************************************************************************/
@@ -750,42 +751,58 @@ IsNumericLiteral(ParseNode *pn)
 {
     return pn->isKind(PNK_NUMBER) ||
            (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
 }
 
 static NumLit
 ExtractNumericLiteral(ParseNode *pn)
 {
+    // The JS grammar treats -42 as -(42) (i.e., with separate grammar
+    // productions) for the unary - and literal 42). However, the asm.js spec
+    // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
+    // so fold the two potential parse nodes into a single double value.
     JS_ASSERT(IsNumericLiteral(pn));
     ParseNode *numberNode;
     double d;
     if (pn->isKind(PNK_NEG)) {
         numberNode = UnaryKid(pn);
         d = -NumberNodeValue(numberNode);
     } else {
         numberNode = pn;
         d = NumberNodeValue(numberNode);
     }
 
+    // The asm.js spec syntactically distinguishes any literal containing a
+    // decimal point or the literal -0 as having double type.
     if (NumberNodeHasFrac(numberNode) || IsNegativeZero(d))
         return NumLit(NumLit::Double, DoubleValue(d));
 
+    // The syntactic checks above rule out these double values.
+    JS_ASSERT(!IsNegativeZero(d));
+    JS_ASSERT(!IsNaN(d));
+
+    // Although doubles can only *precisely* represent 53-bit integers, they
+    // can *imprecisely* represent integers much bigger than an int64_t.
+    // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
+    // is undefined, so test against the integer bounds using doubles.
+    if (d < double(INT32_MIN) || d > double(UINT32_MAX))
+        return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
+
+    // With the above syntactic and range limitations, d is definitely an
+    // integer in the range [INT32_MIN, UINT32_MAX] range.
     int64_t i64 = int64_t(d);
-
     if (i64 >= 0) {
         if (i64 <= INT32_MAX)
             return NumLit(NumLit::Fixnum, Int32Value(i64));
-        if (i64 <= UINT32_MAX)
-            return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
-        return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
-    }
-    if (i64 >= INT32_MIN)
-        return NumLit(NumLit::NegativeInt, Int32Value(i64));
-    return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
+        JS_ASSERT(i64 <= UINT32_MAX);
+        return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
+    }
+    JS_ASSERT(i64 >= INT32_MIN);
+    return NumLit(NumLit::NegativeInt, Int32Value(i64));
 }
 
 static inline bool
 IsLiteralUint32(ParseNode *pn, uint32_t *u32)
 {
     if (!IsNumericLiteral(pn))
         return false;
 
--- a/js/src/jit-test/tests/asm.js/testLiterals.js
+++ b/js/src/jit-test/tests/asm.js/testLiterals.js
@@ -14,16 +14,22 @@ assertEq(asmLink(asmCompile(USE_ASM + 'f
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=1.0e0; e=d; return +e } return f'))(0.1), 0.1);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(d) { d=+d; var e=-1.0e0; e=d; return +e } return f'))(0.1), 0.1);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return 0.0 } function g() { var d=0.1; d=+f(); return +d } return g'))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return -0.0 } function g() { var d=0.1; d=+f(); return +d } return g'))(), -0);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return 10.0 } function g() { var d=0.1; d=+f(); return +d } return g'))(), 10);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f() { return -10.0 } function g() { var d=0.1; d=+f(); return +d } return g'))(), -10.0);
 
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e10; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=9007199254740991e0; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=9007199254740992e0; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=9007199254740993e0; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=-9007199254740991e0; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=-9007199254740992e0; j=i; return j|0 } return f");
+assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=-9007199254740993e0; j=i; return j|0 } return f");
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e100; j=i; return j|0 } return f");
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1e10000; j=i; return j|0 } return f");
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; var j=1000000000000000000; j=i; return j|0 } return f");
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var j=1e0; j=i; return j|0 } return f"))(42), 42);
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; var j=1e9; j=i; return j|0 } return f"))(42), 42);
 
 assertAsmTypeFail(USE_ASM + 'function f() { var i=-2147483649; return i|0 } return f');
 assertAsmTypeFail(USE_ASM + 'function f() { var i=4294967296; return i|0 } return f');