Bug 1044256: SIMD: Add support for unary operators in Odin; r=luke
☠☠ backed out by a9a291cb900b ☠ ☠
authorBenjamin Bouvier <benj@benj.me>
Tue, 07 Oct 2014 14:10:00 +0200
changeset 209152 ab230e36399bdf43f572a6e759bdff4ffdb2d65c
parent 209151 2e15d016544a16ddf87004dc206e93e664de0847
child 209153 b2238670c5bfadfe00fe080a225d7bf3446e03de
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersluke
bugs1044256
milestone35.0a1
Bug 1044256: SIMD: Add support for unary operators in Odin; r=luke
js/src/asmjs/AsmJSValidate.cpp
js/src/builtin/SIMD.h
js/src/jit-test/tests/asm.js/testSIMD.js
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -2422,16 +2422,27 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return nullptr;
         T *ins = T::NewAsmJS(alloc(), lhs, rhs, type);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition *unarySimd(MDefinition *input, MSimdUnaryArith::Operation op, MIRType type)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        MOZ_ASSERT(IsSimdType(input->type()) && input->type() == type);
+        MInstruction *ins = MSimdUnaryArith::NewAsmJS(alloc(), input, op, type);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition *binarySimd(MDefinition *lhs, MDefinition *rhs, MSimdBinaryArith::Operation op,
                             MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type());
         MOZ_ASSERT(lhs->type() == type);
@@ -4848,16 +4859,28 @@ class CheckSimdVectorScalarArgs
                            coercedFormalType.toChars());
         }
         return true;
     }
 };
 
 } // anonymous namespace
 
+static inline bool
+CheckSimdUnary(FunctionCompiler &f, ParseNode *call, Type retType, MSimdUnaryArith::Operation op,
+               MDefinition **def, Type *type)
+{
+    DefinitionVector defs;
+    if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(retType), &defs))
+        return false;
+    *def = f.unarySimd(defs[0], op, retType.toMIRType());
+    *type = retType;
+    return true;
+}
+
 template<class OpEnum>
 static inline bool
 CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, OpEnum op, MDefinition **def,
                 Type *type)
 {
     DefinitionVector argDefs;
     if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
         return false;
@@ -4980,16 +5003,29 @@ CheckSimdOperationCall(FunctionCompiler 
 
       case AsmJSSimdOperation_shiftLeft:
         return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::lsh, def, type);
       case AsmJSSimdOperation_shiftRight:
         return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::rsh, def, type);
       case AsmJSSimdOperation_shiftRightLogical:
         return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::ursh, def, type);
 
+      case AsmJSSimdOperation_abs:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::abs, def, type);
+      case AsmJSSimdOperation_neg:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::neg, def, type);
+      case AsmJSSimdOperation_not:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::not_, def, type);
+      case AsmJSSimdOperation_sqrt:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::sqrt, def, type);
+      case AsmJSSimdOperation_reciprocal:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocal, def, type);
+      case AsmJSSimdOperation_reciprocalSqrt:
+        return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocalSqrt, def, type);
+
       case AsmJSSimdOperation_splat: {
         DefinitionVector defs;
         Type formalType = retType.simdToCoercedScalarType();
         if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(formalType), &defs))
             return false;
         *def = f.splatSimd(defs[0], retType.toMIRType());
         *type = retType;
         return true;
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -115,16 +115,20 @@
 
 #define FOREACH_INT32X4_SIMD_OP(_)   \
     _(fromFloat32x4)                 \
     _(fromFloat32x4Bits)             \
     _(shiftLeft)                     \
     _(shiftRight)                    \
     _(shiftRightLogical)
 #define FOREACH_FLOAT32X4_SIMD_OP(_) \
+    _(abs)                           \
+    _(sqrt)                          \
+    _(reciprocal)                    \
+    _(reciprocalSqrt)                \
     _(fromInt32x4)                   \
     _(fromInt32x4Bits)               \
     _(mul)                           \
     _(div)                           \
     _(max)                           \
     _(min)                           \
     _(lessThanOrEqual)               \
     _(notEqual)                      \
@@ -138,17 +142,19 @@
     _(and)                           \
     _(or)                            \
     _(xor)                           \
     _(select)                        \
     _(splat)                         \
     _(withX)                         \
     _(withY)                         \
     _(withZ)                         \
-    _(withW)
+    _(withW)                         \
+    _(not)                           \
+    _(neg)
 #define FORALL_SIMD_OP(_)            \
     FOREACH_INT32X4_SIMD_OP(_)       \
     FOREACH_FLOAT32X4_SIMD_OP(_)     \
     FOREACH_COMMONX4_SIMD_OP(_)
 
 namespace js {
 
 class SIMDObject : public JSObject
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -19,21 +19,24 @@ const F32M = 'var f4m = f4.mul;'
 const F32D = 'var f4d = f4.div;'
 const FROUND = 'var f32=glob.Math.fround;'
 
 const INT32_MAX = Math.pow(2, 31) - 1;
 const INT32_MIN = INT32_MAX + 1 | 0;
 
 const assertEqFFI = {assertEq:assertEq};
 
-function assertEqX4(real, expected) {
-    assertEq(real.x, expected[0]);
-    assertEq(real.y, expected[1]);
-    assertEq(real.z, expected[2]);
-    assertEq(real.w, expected[3]);
+function assertEqX4(real, expected, assertFunc) {
+    if (typeof assertFunc === 'undefined')
+        assertFunc = assertEq;
+
+    assertFunc(real.x, expected[0]);
+    assertFunc(real.y, expected[1]);
+    assertFunc(real.z, expected[2]);
+    assertFunc(real.w, expected[3]);
 }
 
 function CheckI4(header, code, expected) {
     // code needs to contain a local called x
     header = USE_ASM + I32 + header;
     var lanes = ['x', 'y', 'z', 'w'];
     for (var i = 0; i < 4; ++i) {
         var lane = lanes[i];
@@ -439,16 +442,78 @@ CheckF4(F32S, 'var x=f4(13.37,2,3,4); va
 assertAsmTypeFail('glob', USE_ASM + I32 + "var f4m=i4.mul; function f() {} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.div; function f() {} return f");
 
 CheckF4(F32M, 'var x=f4(1,2,3,4); x=f4m(x,x)', [1,4,9,16]);
 CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]);
 CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]);
 CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]);
 
+// Unary arithmetic operators
+function CheckUnaryF4(op, checkFunc, assertFunc) {
+    var _ = asmLink(asmCompile('glob', USE_ASM + F32 + 'var op=f4.' + op + '; function f(x){x=f4(x); return f4(op(x)); } return f'), this);
+    return function(input) {
+        var simd = SIMD.float32x4(input[0], input[1], input[2], input[3]);
+
+        var exp = input.map(Math.fround).map(checkFunc).map(Math.fround);
+        var obs = _(simd);
+        assertEqX4(obs, exp, assertFunc);
+    }
+}
+
+function CheckUnaryI4(op, checkFunc) {
+    var _ = asmLink(asmCompile('glob', USE_ASM + I32 + 'var op=i4.' + op + '; function f(x){x=i4(x); return i4(op(x)); } return f'), this);
+    return function(input) {
+        var simd = SIMD.int32x4(input[0], input[1], input[2], input[3]);
+        assertEqX4(_(simd), input.map(checkFunc).map(function(x) { return x | 0}));
+    }
+}
+
+CheckUnaryI4('neg', function(x) { return -x })([1, -2, INT32_MIN, INT32_MAX]);
+CheckUnaryI4('not', function(x) { return ~x })([1, -2, INT32_MIN, INT32_MAX]);
+
+var CheckAbs = CheckUnaryF4('abs', Math.abs);
+CheckAbs([1, 42.42, 0.63, 13.37]);
+CheckAbs([NaN, -Infinity, Infinity, 0]);
+
+var CheckNegF = CheckUnaryF4('neg', function(x) { return -x });
+CheckNegF([1, 42.42, 0.63, 13.37]);
+CheckNegF([NaN, -Infinity, Infinity, 0]);
+
+var CheckNotF = CheckUnaryF4('not', (function() {
+    var f32 = new Float32Array(1);
+    var i32 = new Int32Array(f32.buffer);
+    return function(x) {
+        f32[0] = x;
+        i32[0] = ~i32[0];
+        return f32[0];
+    }
+})());
+CheckNotF([1, 42.42, 0.63, 13.37]);
+CheckNotF([NaN, -Infinity, Infinity, 0]);
+
+var CheckSqrt = CheckUnaryF4('sqrt', function(x) { return Math.sqrt(x); });
+CheckSqrt([1, 42.42, 0.63, 13.37]);
+CheckSqrt([NaN, -Infinity, Infinity, 0]);
+
+// Reciprocal and reciprocalSqrt give approximate results
+function assertNear(a, b) {
+    if (a !== a && b === b)
+        throw 'Observed NaN, expected ' + b;
+    if (Math.abs(a - b) > 1e-3)
+        throw 'More than 1e-3 between ' + a + ' and ' + b;
+}
+var CheckRecp = CheckUnaryF4('reciprocal', function(x) { return 1 / x; }, assertNear);
+CheckRecp([1, 42.42, 0.63, 13.37]);
+CheckRecp([NaN, -Infinity, Infinity, 0]);
+
+var CheckRecp = CheckUnaryF4('reciprocalSqrt', function(x) { return 1 / Math.sqrt(x); }, assertNear);
+CheckRecp([1, 42.42, 0.63, 13.37]);
+CheckRecp([NaN, -Infinity, Infinity, 0]);
+
 // Min/Max
 assertAsmTypeFail('glob', USE_ASM + I32 + "var f4m=i4.min; function f() {} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.max; function f() {} return f");
 
 const F32MIN = 'var min = f4.min;'
 const F32MAX = 'var max = f4.max;'
 
 // TODO amend tests once float32x4.min/max is fully specified, for NaN and -0