Bug 1136226 - Inline saturating arithmetic operations. r=sunfish
authorJakob Stoklund Olesen <jolesen@mozilla.com>
Tue, 31 May 2016 09:00:20 -0700
changeset 338734 a019522f497b3d852ece421f4e09548e654d1e7e
parent 338733 8e4f48388c600edd5ee5292b014cedb0b8f7672f
child 338735 500e86461a3cce584c5b8069a2721a95366f8f72
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1136226
milestone49.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 1136226 - Inline saturating arithmetic operations. r=sunfish Add support for inlining addSaturate and subSaturate SIMD operations when compiling SIMD.js.
js/src/jit-test/lib/simd.js
js/src/jit-test/tests/SIMD/binary-arith.js
js/src/jit-test/tests/SIMD/float32x4-binary-arith.js
js/src/jit-test/tests/SIMD/saturate.js
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
--- a/js/src/jit-test/lib/simd.js
+++ b/js/src/jit-test/lib/simd.js
@@ -4,21 +4,21 @@ if (!this.hasOwnProperty("SIMD"))
 function booleanBinaryX4(op, v, w) {
     var arr = [];
     var [varr, warr] = [simdToArray(v), simdToArray(w)];
     for (var i = 0; i < 4; i++)
         arr[i] = op(varr[i], warr[i]);
     return arr;
 }
 
-function binaryX4(op, v, w) {
+function binaryX(op, v, w) {
     var arr = [];
     var [varr, warr] = [simdToArray(v), simdToArray(w)];
     [varr, warr] = [varr.map(Math.fround), warr.map(Math.fround)];
-    for (var i = 0; i < 4; i++)
+    for (var i = 0; i < varr.length; i++)
         arr[i] = op(varr[i], warr[i]);
     return arr.map(Math.fround);
 }
 
 function unaryX4(op, v, coerceFunc) {
     var arr = [];
     var varr = simdToArray(v).map(coerceFunc);
     for (var i = 0; i < 4; i++)
--- a/js/src/jit-test/tests/SIMD/binary-arith.js
+++ b/js/src/jit-test/tests/SIMD/binary-arith.js
@@ -5,19 +5,19 @@ setJitCompilerOption("ion.warmup.trigger
 function f() {
     var i1 = SIMD.Int32x4(1, 2, 3, 4);
     var i2 = SIMD.Int32x4(4, 3, 2, 1);
 
     var f1 = SIMD.Float32x4(1, 2, 3, 4);
     var f2 = SIMD.Float32x4(4, 3, 2, 1);
 
     for (var i = 0; i < 150; i++) {
-        assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX4((x, y) => x + y, f1, f2));
-        assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX4((x, y) => x - y, f1, f2));
-        assertEqX4(SIMD.Float32x4.mul(f1, f2), binaryX4((x, y) => x * y, f1, f2));
+        assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX((x, y) => x + y, f1, f2));
+        assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX((x, y) => x - y, f1, f2));
+        assertEqX4(SIMD.Float32x4.mul(f1, f2), binaryX((x, y) => x * y, f1, f2));
 
-        assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX4((x, y) => x + y, i1, i2));
-        assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX4((x, y) => x - y, i1, i2));
-        assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX4((x, y) => x * y, i1, i2));
+        assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX((x, y) => x + y, i1, i2));
+        assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX((x, y) => x - y, i1, i2));
+        assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX((x, y) => x * y, i1, i2));
     }
 }
 
 f();
--- a/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js
+++ b/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js
@@ -17,17 +17,17 @@ function minNum(x, y) {
         return x;
     return Math.min(x, y);
 }
 
 function f() {
     var f1 = SIMD.Float32x4(1, 2, 3, 4);
     var f2 = SIMD.Float32x4(4, 3, 2, 1);
     for (var i = 0; i < 150; i++) {
-        assertEqX4(SIMD.Float32x4.div(f1, f2), binaryX4((x, y) => x / y, f1, f2));
-        assertEqX4(SIMD.Float32x4.min(f1, f2), binaryX4(Math.min, f1, f2));
-        assertEqX4(SIMD.Float32x4.max(f1, f2), binaryX4(Math.max, f1, f2));
-        assertEqX4(SIMD.Float32x4.minNum(f1, f2), binaryX4(minNum, f1, f2));
-        assertEqX4(SIMD.Float32x4.maxNum(f1, f2), binaryX4(maxNum, f1, f2));
+        assertEqX4(SIMD.Float32x4.div(f1, f2), binaryX((x, y) => x / y, f1, f2));
+        assertEqX4(SIMD.Float32x4.min(f1, f2), binaryX(Math.min, f1, f2));
+        assertEqX4(SIMD.Float32x4.max(f1, f2), binaryX(Math.max, f1, f2));
+        assertEqX4(SIMD.Float32x4.minNum(f1, f2), binaryX(minNum, f1, f2));
+        assertEqX4(SIMD.Float32x4.maxNum(f1, f2), binaryX(maxNum, f1, f2));
     }
 }
 
 f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/saturate.js
@@ -0,0 +1,37 @@
+load(libdir + 'simd.js');
+
+setJitCompilerOption("ion.warmup.trigger", 50);
+
+const INT8_MIN = -128;
+const INT8_MAX = 127;
+const UINT8_MAX = 255;
+
+function sat8(x) {
+    if (x < INT8_MIN) return INT8_MIN;
+    if (x > INT8_MAX) return INT8_MAX;
+    return x;
+}
+
+function usat8(x) {
+    if (x < 0) return 0;
+    if (x > UINT8_MAX) return UINT8_MAX;
+    return x;
+}
+
+function f() {
+    var i1 = SIMD.Int8x16(1, 100, 3, 4);
+    var i2 = SIMD.Int8x16(4, 30, 2, 1);
+
+    var u1 = SIMD.Uint8x16(1, 2, 3, 4);
+    var u2 = SIMD.Uint8x16(4, 3, 2, 1);
+
+    for (var i = 0; i < 150; i++) {
+        assertEqX4(SIMD.Int8x16.addSaturate(i1, i2), binaryX((x, y) => sat8(x + y), i1, i2));
+        assertEqX4(SIMD.Int8x16.subSaturate(i1, i2), binaryX((x, y) => sat8(x - y), i1, i2));
+
+        assertEqX4(SIMD.Uint8x16.addSaturate(u1, u2), binaryX((x, y) => usat8(x + y), u1, u2));
+        assertEqX4(SIMD.Uint8x16.subSaturate(u1, u2), binaryX((x, y) => usat8(x - y), u1, u2));
+    }
+}
+
+f();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -913,16 +913,18 @@ class IonBuilder
                                        InlineTypedObject* templateObj);
     MDefinition* convertToBooleanSimdLane(MDefinition* scalar);
 
     InliningStatus inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type);
 
     template <typename T>
     InliningStatus inlineSimdBinary(CallInfo& callInfo, JSNative native,
                                     typename T::Operation op, SimdType type);
+    InliningStatus inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
+                                              MSimdBinarySaturating::Operation op, SimdType type);
     InliningStatus inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
                                    SimdType type);
     InliningStatus inlineSimdComp(CallInfo& callInfo, JSNative native,
                                   MSimdBinaryComp::Operation op, SimdType type);
     InliningStatus inlineSimdUnary(CallInfo& callInfo, JSNative native,
                                    MSimdUnaryArith::Operation op, SimdType type);
     InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType type);
     InliningStatus inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType type);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3263,19 +3263,22 @@ IonBuilder::inlineSimd(CallInfo& callInf
         return inlineSimdBinary<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_min,
                                                   type);
       case SimdOperation::Fn_maxNum:
         return inlineSimdBinary<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_maxNum,
                                                   type);
       case SimdOperation::Fn_minNum:
         return inlineSimdBinary<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_minNum,
                                                   type);
+
+        // Binary saturating.
       case SimdOperation::Fn_addSaturate:
+        return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::add, type);
       case SimdOperation::Fn_subSaturate:
-        MOZ_CRASH("NYI");
+        return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::sub, type);
 
         // Binary bitwise.
       case SimdOperation::Fn_and:
         return inlineSimdBinary<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::and_,
                                                     type);
       case SimdOperation::Fn_or:
         return inlineSimdBinary<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::or_,
                                                     type);
@@ -3558,16 +3561,33 @@ IonBuilder::inlineSimdBinary(CallInfo& c
 
     MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
     MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
 
     T* ins = T::New(alloc(), lhs, rhs, op);
     return boxSimd(callInfo, ins, templateObj);
 }
 
+// Inline a binary SIMD operation where both arguments are SIMD types.
+IonBuilder::InliningStatus
+IonBuilder::inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
+                                       MSimdBinarySaturating::Operation op, SimdType type)
+{
+    InlineTypedObject* templateObj = nullptr;
+    if (!canInlineSimd(callInfo, native, 2, &templateObj))
+        return InliningStatus_NotInlined;
+
+    MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
+    MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
+
+    MSimdBinarySaturating* ins =
+      MSimdBinarySaturating::New(alloc(), lhs, rhs, op, GetSimdSign(type));
+    return boxSimd(callInfo, ins, templateObj);
+}
+
 // Inline a SIMD shiftByScalar operation.
 IonBuilder::InliningStatus
 IonBuilder::inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
                             SimdType type)
 {
     InlineTypedObject* templateObj = nullptr;
     if (!canInlineSimd(callInfo, native, 2, &templateObj))
         return InliningStatus_NotInlined;