Bug 1088709: Add SIMD.{float,int}32x4.{load,store} to the interpreter; r=till
authorBenjamin Bouvier <benj@benj.me>
Fri, 21 Nov 2014 12:11:59 +0100
changeset 241221 0432a14c9283f68b3f7487f20d14f9214b3fe8b4
parent 241220 1ff8792d5db27809e855ea14430b7b56450f2034
child 241222 74527e0493c585acfb6eebd8d6aa622b7939d6a2
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1088709
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 1088709: Add SIMD.{float,int}32x4.{load,store} to the interpreter; r=till
js/src/builtin/SIMD.cpp
js/src/builtin/SIMD.h
js/src/tests/ecma_6/TypedObject/simd/load.js
js/src/tests/ecma_6/TypedObject/simd/shell.js
js/src/tests/ecma_6/TypedObject/simd/store.js
js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -966,16 +966,94 @@ Float32x4Select(JSContext *cx, unsigned 
     int32_t orInt[Int32x4::lanes];
     for (unsigned i = 0; i < Int32x4::lanes; i++)
         orInt[i] = Or<int32_t>::apply(tr[i], fr[i]);
 
     float *result = reinterpret_cast<float *>(orInt);
     return StoreResult<Float32x4>(cx, args, result);
 }
 
+template<class VElem, unsigned NumElem>
+static bool
+TypedArrayDataPtrFromArgs(JSContext *cx, const CallArgs &args, VElem **data)
+{
+    if (!args[0].isObject())
+        return ErrorBadArgs(cx);
+
+    JSObject &argobj = args[0].toObject();
+    if (!argobj.is<TypedArrayObject>())
+        return ErrorBadArgs(cx);
+
+    Rooted<TypedArrayObject*> typedArray(cx, &argobj.as<TypedArrayObject>());
+
+    int32_t index;
+    if (!ToInt32(cx, args[1], &index))
+        return false;
+
+    int32_t byteStart = index * typedArray->bytesPerElement();
+    if (byteStart < 0 || (uint32_t(byteStart) + NumElem * sizeof(VElem)) > typedArray->byteLength())
+        return ErrorBadArgs(cx);
+
+    *data = reinterpret_cast<VElem*>(static_cast<char*>(typedArray->viewData()) + byteStart);
+    return true;
+}
+
+template<class V, unsigned NumElem>
+static bool
+Load(JSContext *cx, unsigned argc, Value *vp)
+{
+    typedef typename V::Elem Elem;
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 2)
+        return ErrorBadArgs(cx);
+
+    Elem *typedArrayData = nullptr;
+    if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
+        return false;
+
+    Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
+    MOZ_ASSERT(typeDescr);
+    Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
+    if (!result)
+        return false;
+
+    Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
+    for (unsigned i = 0; i < NumElem; i++)
+        dest[i] = typedArrayData[i];
+
+    args.rval().setObject(*result);
+    return true;
+}
+
+template<class V, unsigned NumElem>
+static bool
+Store(JSContext *cx, unsigned argc, Value *vp)
+{
+    typedef typename V::Elem Elem;
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 3)
+        return ErrorBadArgs(cx);
+
+    Elem *typedArrayData = nullptr;
+    if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
+        return false;
+
+    if (!IsVectorObject<V>(args[2]))
+        return ErrorBadArgs(cx);
+
+    Elem *src = TypedObjectMemory<Elem*>(args[2]);
+    for (unsigned i = 0; i < NumElem; i++)
+        typedArrayData[i] = src[i];
+
+    args.rval().setObject(args[2].toObject());
+    return true;
+}
+
 #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 bool                                                                \
 js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp)  \
 {                                                                   \
     return Func(cx, argc, vp);                                      \
 }
 FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION)
 #undef DEFINE_SIMD_FLOAT32x4_FUNCTION
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -36,22 +36,30 @@
   V(add, (BinaryFunc<Float32x4, Add, Float32x4>), 2, 0)                             \
   V(and, (CoercedBinaryFunc<Float32x4, Int32x4, And, Float32x4>), 2, 0)             \
   V(div, (BinaryFunc<Float32x4, Div, Float32x4>), 2, 0)                             \
   V(equal, (CompareFunc<Float32x4, Equal>), 2, 0)                                   \
   V(greaterThan, (CompareFunc<Float32x4, GreaterThan>), 2, 0)                       \
   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(min, (BinaryFunc<Float32x4, Minimum, 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)                                          \
   V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2, 0)                             \
   V(withX, (FuncWith<Float32x4, WithX>), 2, 0)                                      \
   V(withY, (FuncWith<Float32x4, WithY>), 2, 0)                                      \
   V(withZ, (FuncWith<Float32x4, WithZ>), 2, 0)                                      \
   V(withW, (FuncWith<Float32x4, WithW>), 2, 0)                                      \
   V(xor, (CoercedBinaryFunc<Float32x4, Int32x4, Xor, Float32x4>), 2, 0)
 
 #define FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                          \
@@ -80,22 +88,30 @@
   V(splat, (FuncSplat<Int32x4>), 0, 0)
 
 #define INT32X4_BINARY_FUNCTION_LIST(V)                                             \
   V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2, 0)                                 \
   V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0)                                 \
   V(equal, (CompareFunc<Int32x4, Equal>), 2, 0)                                     \
   V(greaterThan, (CompareFunc<Int32x4, GreaterThan>), 2, 0)                         \
   V(lessThan, (CompareFunc<Int32x4, LessThan>), 2, 0)                               \
+  V(load,    (Load<Int32x4, 4>), 2, 0)                                              \
+  V(loadXYZ, (Load<Int32x4, 3>), 2, 0)                                              \
+  V(loadXY,  (Load<Int32x4, 2>), 2, 0)                                              \
+  V(loadX,   (Load<Int32x4, 1>), 2, 0)                                              \
   V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2, 0)                                 \
   V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2, 0)                                   \
   V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2, 0)                                 \
   V(shiftLeft, (Int32x4BinaryScalar<ShiftLeft>), 2, 0)                              \
   V(shiftRight, (Int32x4BinaryScalar<ShiftRight>), 2, 0)                            \
   V(shiftRightLogical, (Int32x4BinaryScalar<ShiftRightLogical>), 2, 0)              \
+  V(store,    (Store<Int32x4, 4>), 3, 0)                                            \
+  V(storeXYZ, (Store<Int32x4, 3>), 3, 0)                                            \
+  V(storeXY,  (Store<Int32x4, 2>), 3, 0)                                            \
+  V(storeX,   (Store<Int32x4, 1>), 3, 0)                                            \
   V(withFlagX, (FuncWith<Int32x4, WithFlagX>), 2, 0)                                \
   V(withFlagY, (FuncWith<Int32x4, WithFlagY>), 2, 0)                                \
   V(withFlagZ, (FuncWith<Int32x4, WithFlagZ>), 2, 0)                                \
   V(withFlagW, (FuncWith<Int32x4, WithFlagW>), 2, 0)                                \
   V(withX, (FuncWith<Int32x4, WithX>), 2, 0)                                        \
   V(withY, (FuncWith<Int32x4, WithY>), 2, 0)                                        \
   V(withZ, (FuncWith<Int32x4, WithZ>), 2, 0)                                        \
   V(withW, (FuncWith<Int32x4, WithW>), 2, 0)                                        \
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/simd/load.js
@@ -0,0 +1,154 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Our float32array will have 16 elements
+const SIZE_ARRAY = 16;
+
+// 1 float32 == 4 bytes
+const SIZE_BYTES = SIZE_ARRAY * 4;
+
+function MakeComparator(kind, arr) {
+    var bpe = arr.BYTES_PER_ELEMENT;
+    var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr;
+
+    // Size in bytes of a single element in the SIMD vector.
+    var sizeOfLaneElem;
+    // Typed array constructor corresponding to the SIMD kind.
+    var typedArrayCtor;
+    switch (kind) {
+      case 'int32x4':
+        sizeOfLaneElem = 4;
+        typedArrayCtor = Int32Array;
+        break;
+      case 'float32x4':
+        sizeOfLaneElem = 4;
+        typedArrayCtor = Float32Array;
+        break;
+      default:
+        assertEq(true, false, "unknown SIMD kind");
+    }
+
+    // Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
+    // these bytes as a typed array equivalent to the typed SIMD vector.
+    var slice = function(start, numElemToRead) {
+        // Read enough bytes
+        var startBytes = start * bpe;
+        var endBytes = startBytes + numElemToRead * sizeOfLaneElem;
+        var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes);
+
+        // If length is less than SIZE_BYTES bytes, fill with 0.
+        // This is needed for loadX, loadXY, loadXYZ which do only partial
+        // reads.
+        for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0;
+        assertEq(asArray.length, SIZE_BYTES);
+
+        return new typedArrayCtor(new Uint8Array(asArray).buffer);
+    }
+
+    return {
+        loadX: function(index) {
+            var v = SIMD[kind].loadX(arr, index);
+            assertEqX4(v, slice(index, 1));
+        },
+
+        loadXY: function(index) {
+            var v = SIMD[kind].loadXY(arr, index);
+            assertEqX4(v, slice(index, 2));
+        },
+
+        loadXYZ: function(index) {
+            var v = SIMD[kind].loadXYZ(arr, index);
+            assertEqX4(v, slice(index, 3));
+        },
+
+        load: function(index) {
+            var v = SIMD[kind].load(arr, index);
+            assertEqX4(v, slice(index, 4));
+        }
+    }
+}
+
+function testLoad(kind, TA) {
+    for (var i = SIZE_ARRAY; i--;)
+        TA[i] = i;
+
+    for (var ta of [
+                    new Uint8Array(TA.buffer),
+                    new Int8Array(TA.buffer),
+                    new Uint16Array(TA.buffer),
+                    new Int16Array(TA.buffer),
+                    new Uint32Array(TA.buffer),
+                    new Int32Array(TA.buffer),
+                    new Float32Array(TA.buffer),
+                    new Float64Array(TA.buffer)
+                   ])
+    {
+        // Invalid args
+        assertThrowsInstanceOf(() => SIMD[kind].load(), TypeError);
+        assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError);
+        assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError);
+        assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), TypeError);
+
+        // Valid and invalid reads
+        var C = MakeComparator(kind, ta);
+        var bpe = ta.BYTES_PER_ELEMENT;
+
+        var lastValidArgLoadX   = (SIZE_BYTES - 4)  / bpe | 0;
+        var lastValidArgLoadXY  = (SIZE_BYTES - 8)  / bpe | 0;
+        var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
+        var lastValidArgLoad    = (SIZE_BYTES - 16) / bpe | 0;
+
+        C.load(0);
+        C.load(1);
+        C.load(2);
+        C.load(3);
+        C.load(lastValidArgLoad);
+        assertThrowsInstanceOf(() => SIMD[kind].load(ta, lastValidArgLoad + 1), TypeError);
+
+        C.loadX(0);
+        C.loadX(1);
+        C.loadX(2);
+        C.loadX(3);
+        C.loadX(lastValidArgLoadX);
+        assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), TypeError);
+
+        C.loadXY(0);
+        C.loadXY(1);
+        C.loadXY(2);
+        C.loadXY(3);
+        C.loadXY(lastValidArgLoadXY);
+        assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), TypeError);
+
+        C.loadXYZ(0);
+        C.loadXYZ(1);
+        C.loadXYZ(2);
+        C.loadXYZ(3);
+        C.loadXYZ(lastValidArgLoadXYZ);
+        assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), TypeError);
+    }
+
+    // Test ToInt32 behavior
+    var v = SIMD[kind].load(TA, 12.5);
+    assertEqX4(v, [12, 13, 14, 15]);
+
+    var obj = {
+        valueOf: function() { return 12 }
+    }
+    var v = SIMD[kind].load(TA, obj);
+    assertEqX4(v, [12, 13, 14, 15]);
+
+    var obj = {
+        valueOf: function() { throw new TypeError("i ain't a number"); }
+    }
+    assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
+}
+
+testLoad('float32x4', new Float32Array(SIZE_ARRAY));
+testLoad('int32x4', new Int32Array(SIZE_ARRAY));
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedObject/simd/shell.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/shell.js
@@ -0,0 +1,12 @@
+function assertEqX4(v, arr) {
+    try {
+        assertEq(v.x, arr[0]);
+        assertEq(v.y, arr[1]);
+        assertEq(v.z, arr[2]);
+        assertEq(v.w, arr[3]);
+    } catch (e) {
+        print("stack trace:", e.stack);
+        throw e;
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/simd/store.js
@@ -0,0 +1,85 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// As SIMD.*.store is entirely symmetric to SIMD.*.load, this file just
+// contains basic tests to store on one single TypedArray kind, while load is
+// exhaustively tested. See load.js for more details.
+
+const POISON = 42;
+
+function reset(ta) {
+    for (var i = 0; i < ta.length; i++)
+        ta[i] = POISON + i;
+}
+
+function assertChanged(ta, from, expected) {
+    var i = 0;
+    for (; i < from; i++)
+        assertEq(ta[i], POISON + i);
+    for (; i < from + expected.length; i++)
+        assertEq(ta[i], expected[i - from]);
+    for (; i < ta.length; i++)
+        assertEq(ta[i], POISON + i);
+}
+
+function testStore(ta, kind, i, v) {
+    reset(ta);
+    SIMD[kind].storeX(ta, i, v);
+    assertChanged(ta, i, [v.x]);
+
+    reset(ta);
+    SIMD[kind].storeXY(ta, i, v);
+    assertChanged(ta, i, [v.x, v.y]);
+
+    reset(ta);
+    SIMD[kind].storeXYZ(ta, i, v);
+    assertChanged(ta, i, [v.x, v.y, v.z]);
+
+    reset(ta);
+    SIMD[kind].store(ta, i, v);
+    assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
+}
+
+function testStoreInt32x4() {
+    var I32 = new Int32Array(16);
+
+    var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31));
+    testStore(I32, 'int32x4', 0, v);
+    testStore(I32, 'int32x4', 1, v);
+    testStore(I32, 'int32x4', 2, v);
+    testStore(I32, 'int32x4', 12, v);
+
+    assertThrowsInstanceOf(() => SIMD.int32x4.store(I32), TypeError);
+    assertThrowsInstanceOf(() => SIMD.int32x4.store(I32, 0), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float32x4.store(I32, 0, v), TypeError);
+}
+
+function testStoreFloat32x4() {
+    var F32 = new Float32Array(16);
+
+    var v = SIMD.float32x4(1,2,3,4);
+    testStore(F32, 'float32x4', 0, v);
+    testStore(F32, 'float32x4', 1, v);
+    testStore(F32, 'float32x4', 2, v);
+    testStore(F32, 'float32x4', 12, v);
+
+    var v = SIMD.float32x4(NaN, -0, -Infinity, 5e-324);
+    testStore(F32, 'float32x4', 0, v);
+    testStore(F32, 'float32x4', 1, v);
+    testStore(F32, 'float32x4', 2, v);
+    testStore(F32, 'float32x4', 12, v);
+
+    assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError);
+    assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
+}
+
+testStoreInt32x4();
+testStoreFloat32x4();
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
@@ -3,23 +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 assertEq4(v, arr) {
-    assertEq(v.x, arr[0]);
-    assertEq(v.y, arr[1]);
-    assertEq(v.z, arr[2]);
-    assertEq(v.w, arr[3]);
-}
-
 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]];
 }
 
@@ -33,26 +26,26 @@ function testSwizzleForType(type) {
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, 4)  , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(0, 1, 2, 3, v)  , TypeError);
 
     // Test all possible swizzles.
     var x, y, z, w;
     for (var i = 0; i < Math.pow(4, 4); i++) {
         [x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
-        assertEq4(type.swizzle(v, x, y, z, w), swizzle(simdToArray(v), x, y, z, w));
+        assertEqX4(type.swizzle(v, x, y, z, w), swizzle(simdToArray(v), x, y, z, w));
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEq4(type.swizzle(v, obj, obj, obj, obj), swizzle(simdToArray(v), 0, 1, 2, 3));
+    assertEqX4(type.swizzle(v, obj, obj, obj, obj), swizzle(simdToArray(v), 0, 1, 2, 3));
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, obj), Error);
 }
 
@@ -95,28 +88,28 @@ function testShuffleForType(type) {
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, 8)  , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, 0, 1, 2, 7, rhs)  , TypeError);
 
     // Test all possible shuffles.
     var x, y, z, w;
     for (var i = 0; i < Math.pow(8, 4); i++) {
         [x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
-        assertEq4(type.shuffle(lhs, rhs, x, y, z, w),
-                  shuffle(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
+        assertEqX4(type.shuffle(lhs, rhs, x, y, z, w),
+                   shuffle(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEq4(type.shuffle(lhs, rhs, obj, obj, obj, obj),
-              shuffle(simdToArray(lhs),simdToArray(rhs), 0, 1, 2, 3));
+    assertEqX4(type.shuffle(lhs, rhs, obj, obj, obj, obj),
+               shuffle(simdToArray(lhs),simdToArray(rhs), 0, 1, 2, 3));
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, obj), Error);
 }