--- 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