Bug 1094855: SIMD: Add minNum/maxNum and update min/max in the interpreter; r=till
authorBenjamin Bouvier <benj@benj.me>
Fri, 21 Nov 2014 17:27:17 +0100
changeset 216928 e21104ba92054fe2cd2ffd69d4fd7fb164a52cba
parent 216927 c98a8847f6e2ae576f29f1fcc0b78c358ceb1e47
child 216929 cecc072d44bccea803829f3d4fdbfceb52e6c8c8
push id27868
push userkwierso@gmail.com
push dateSat, 22 Nov 2014 00:36:06 +0000
treeherdermozilla-central@7ab92d922d19 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1094855
milestone36.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 1094855: SIMD: Add minNum/maxNum and update min/max in the interpreter; r=till
js/src/builtin/SIMD.cpp
js/src/builtin/SIMD.h
js/src/jsmath.cpp
js/src/jsmath.h
js/src/tests/ecma_6/TypedObject/simd/float32x4-minmax.js
js/src/tests/ecma_6/TypedObject/simd/float32x4max.js
js/src/tests/ecma_6/TypedObject/simd/float32x4min.js
js/src/tests/ecma_6/TypedObject/simd/int32x4select.js
js/src/tests/ecma_6/TypedObject/simd/shell.js
js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -503,21 +503,29 @@ struct Div {
     static inline T apply(T l, T r) { return l / r; }
 };
 template<typename T>
 struct Mul {
     static inline T apply(T l, T r) { return l * r; }
 };
 template<typename T>
 struct Minimum {
-    static inline T apply(T l, T r) { return l < r ? l : r; }
+    static inline T apply(T l, T r) { return math_min_impl(l, r); }
+};
+template<typename T>
+struct MinNum {
+    static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
 };
 template<typename T>
 struct Maximum {
-    static inline T apply(T l, T r) { return l > r ? l : r; }
+    static inline T apply(T l, T r) { return math_max_impl(l, r); }
+};
+template<typename T>
+struct MaxNum {
+    static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
 };
 template<typename T>
 struct LessThan {
     static inline int32_t apply(T l, T r) { return l < r ? 0xFFFFFFFF : 0x0; }
 };
 template<typename T>
 struct LessThanOrEqual {
     static inline int32_t apply(T l, T r) { return l <= r ? 0xFFFFFFFF : 0x0; }
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -41,17 +41,19 @@
   V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual>), 2, 0)         \
   V(lessThan, (CompareFunc<Float32x4, LessThan>), 2, 0)                             \
   V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual>), 2, 0)               \
   V(load,    (Load<Float32x4, 4>), 2, 0)                                            \
   V(loadXYZ, (Load<Float32x4, 3>), 2, 0)                                            \
   V(loadXY,  (Load<Float32x4, 2>), 2, 0)                                            \
   V(loadX,   (Load<Float32x4, 1>), 2, 0)                                            \
   V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2, 0)                         \
+  V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2, 0)                       \
   V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2, 0)                         \
+  V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2, 0)                       \
   V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2, 0)                             \
   V(notEqual, (CompareFunc<Float32x4, NotEqual>), 2, 0)                             \
   V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0)               \
   V(scale, (FuncWith<Float32x4, Scale>), 2, 0)                                      \
   V(store,    (Store<Float32x4, 4>), 3, 0)                                          \
   V(storeXYZ, (Store<Float32x4, 3>), 3, 0)                                          \
   V(storeXY,  (Store<Float32x4, 2>), 3, 0)                                          \
   V(storeX,   (Store<Float32x4, 1>), 3, 0)                                          \
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -560,18 +560,18 @@ js::math_log(JSContext *cx, unsigned arg
     if (!mathCache)
         return false;
 
     double z = math_log_impl(mathCache, x);
     args.rval().setNumber(z);
     return true;
 }
 
-static double
-max_double(double x, double y)
+double
+js::math_max_impl(double x, double y)
 {
     // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
     if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
         return x;
     return y;
 }
 
 bool
@@ -579,24 +579,24 @@ js::math_max(JSContext *cx, unsigned arg
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     double maxval = NegativeInfinity<double>();
     for (unsigned i = 0; i < args.length(); i++) {
         double x;
         if (!ToNumber(cx, args[i], &x))
             return false;
-        maxval = max_double(x, maxval);
+        maxval = math_max_impl(x, maxval);
     }
     args.rval().setNumber(maxval);
     return true;
 }
 
-static double
-min_double(double x, double y)
+double
+js::math_min_impl(double x, double y)
 {
     // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
     if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
         return x;
     return y;
 }
 
 bool
@@ -604,36 +604,36 @@ js::math_min(JSContext *cx, unsigned arg
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     double minval = PositiveInfinity<double>();
     for (unsigned i = 0; i < args.length(); i++) {
         double x;
         if (!ToNumber(cx, args[i], &x))
             return false;
-        minval = min_double(x, minval);
+        minval = math_min_impl(x, minval);
     }
     args.rval().setNumber(minval);
     return true;
 }
 
 bool
 js::minmax_impl(JSContext *cx, bool max, HandleValue a, HandleValue b, MutableHandleValue res)
 {
     double x, y;
 
     if (!ToNumber(cx, a, &x))
         return false;
     if (!ToNumber(cx, b, &y))
         return false;
 
     if (max)
-        res.setNumber(max_double(x, y));
+        res.setNumber(math_max_impl(x, y));
     else
-        res.setNumber(min_double(x, y));
+        res.setNumber(math_min_impl(x, y));
 
     return true;
 }
 
 double
 js::powi(double x, int y)
 {
     unsigned n = (y < 0) ? -y : y;
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -115,19 +115,25 @@ extern bool
 math_random(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_abs_handle(JSContext *cx, js::HandleValue v, js::MutableHandleValue r);
 
 extern bool
 math_abs(JSContext *cx, unsigned argc, js::Value *vp);
 
+extern double
+math_max_impl(double x, double y);
+
 extern bool
 math_max(JSContext *cx, unsigned argc, js::Value *vp);
 
+extern double
+math_min_impl(double x, double y);
+
 extern bool
 math_min(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_sqrt(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_pow_handle(JSContext *cx, js::HandleValue base, js::HandleValue power,
rename from js/src/tests/ecma_6/TypedObject/simd/float32x4max.js
rename to js/src/tests/ecma_6/TypedObject/simd/float32x4-minmax.js
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4max.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4-minmax.js
@@ -1,35 +1,54 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
+
 var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
+
+function testMaxFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
+}
+function testMinFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
+}
 
-var summary = 'float32x4 max';
+function maxNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.max(x, y);
+}
+
+function minNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.min(x, y);
+}
+
+function testMaxNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
+}
+function testMinNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.minNum, minNum);
+}
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
-  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
-
-  var a = float32x4(1, 20, 30, 4);
-  var b = float32x4(10, 2, 3, 40);
-  var c = SIMD.float32x4.max(a, b);
-  assertEq(c.x, 10);
-  assertEq(c.y, 20);
-  assertEq(c.z, 30);
-  assertEq(c.w, 40);
-
-  var d = float32x4(9.999, 2.1234, 30.4443, 4);
-  var e = float32x4(10, 2.1233, 30.4444, 4.0001);
-  var f = float32x4.max(d, e);
-  assertEq(f.x, 10);
-  assertEq(f.y, Math.fround(2.1234));
-  assertEq(f.z, Math.fround(30.4444));
-  assertEq(f.w, Math.fround(4.0001));
+  for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
+                  [float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
+                  [float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
+  {
+      testMinFloat32(v, w);
+      testMaxFloat32(v, w);
+      testMinNumFloat32(v, w);
+      testMaxNumFloat32(v, w);
+  }
 
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
deleted file mode 100644
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4min.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'float32x4 min';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
-  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
-
-  var a = float32x4(1, 20, 3, 40);
-  var b = float32x4(10, 2, 30, 4);
-  var c = SIMD.float32x4.min(a, b);
-  assertEq(c.x, 1);
-  assertEq(c.y, 2);
-  assertEq(c.z, 3);
-  assertEq(c.w, 4);
-
-  var d = float32x4(1.4321, 20.5567, 30.8999, 4.0002);
-  var e = float32x4(1.432, 20.5568, 30.8998, 4.0001);
-  var f = float32x4.min(d, e);
-  assertEq(f.x, Math.fround(1.432));
-  assertEq(f.y, Math.fround(20.5567));
-  assertEq(f.z, Math.fround(30.8998));
-  assertEq(f.w, Math.fround(4.0001));
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4select.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4select.js
@@ -1,17 +1,14 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 1060437;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'int32x4 select';
 
-const INT32_MAX = Math.pow(2, 31) - 1;
-const INT32_MIN = INT32_MAX + 1 | 0;
-
 function test() {
   print(BUGNUMBER + ": " + summary);
 
   var a = int32x4(0,4,9,16)
   var b = int32x4(1,2,3,4)
   var sel_ttff = int32x4.bool(true, true, false, false);
   var c = SIMD.int32x4.select(sel_ttff,a,b);
   assertEq(c.x, 0);
--- a/js/src/tests/ecma_6/TypedObject/simd/shell.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/shell.js
@@ -5,8 +5,26 @@ function assertEqX4(v, arr) {
         assertEq(v.z, arr[2]);
         assertEq(v.w, arr[3]);
     } catch (e) {
         print("stack trace:", e.stack);
         throw e;
     }
 }
 
+function simdToArray(v) {
+    return [v.x, v.y, v.z, v.w];
+}
+
+const INT32_MAX = Math.pow(2, 31) - 1;
+const INT32_MIN = -Math.pow(2, 31);
+assertEq(INT32_MAX + 1 | 0, INT32_MIN);
+
+function testBinaryFunc(v, w, simdFunc, func) {
+    var varr = simdToArray(v);
+    var warr = simdToArray(w);
+
+    var observed = simdToArray(simdFunc(v, w));
+    var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); });
+
+    for (var i = 0; i < observed.length; i++)
+        assertEq(observed[i], expected[i]);
+}
--- a/js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
@@ -3,20 +3,16 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
-function simdToArray(v) {
-    return [v.x, v.y, v.z, v.w];
-}
-
 function swizzle(arr, x, y, z, w) {
     return [arr[x], arr[y], arr[z], arr[w]];
 }
 
 function testSwizzleForType(type) {
     var v = type(1,2,3,4);
 
     assertThrowsInstanceOf(() => type.swizzle()               , TypeError);