Bug 1083238: Replace SIMD's shuffle/shuffleMix by swizzle/shuffle in the interpreter; r=till
authorBenjamin Bouvier <benj@benj.me>
Fri, 17 Oct 2014 12:22:21 +0200
changeset 211018 5e32e458318f8593d9ecccd52f8ff941c1d942e2
parent 211017 6dd9100871c15b51ddeda3420ffffb0a50462d8c
child 211019 ce11ac061a1bdd1071615a878bce8cc0300dd178
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerstill
bugs1083238
milestone36.0a1
Bug 1083238: Replace SIMD's shuffle/shuffleMix by swizzle/shuffle in the interpreter; r=till
js/src/builtin/SIMD.cpp
js/src/builtin/SIMD.h
js/src/js.msg
js/src/tests/ecma_6/TypedObject/simd/float32x4shuffle.js
js/src/tests/ecma_6/TypedObject/simd/float32x4shufflemix.js
js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js
js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js
js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -966,63 +966,70 @@ FuncWith(JSContext *cx, unsigned argc, V
         for (unsigned i = 0; i < V::lanes; i++)
             result[i] = OpWith<Elem>::apply(i, withAsBool, val[i]);
     }
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename V>
 static bool
-FuncShuffle(JSContext *cx, unsigned argc, Value *vp)
+Swizzle(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 2 && args.length() != 3)
+    if (args.length() != (V::lanes + 1) || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    // Let L be V::Lanes. Each lane can contain L lanes, so the log2(L) first
-    // bits select the first lane, the next log2(L) the second, and so on.
-    const uint32_t SELECT_SHIFT = FloorLog2(V::lanes);
-    const uint32_t SELECT_MASK  = V::lanes - 1;
-    const int32_t MAX_MASK_VALUE = int32_t(pow(double(V::lanes), double(V::lanes))) - 1;
-    MOZ_ASSERT(MAX_MASK_VALUE > 0);
+    int32_t lanes[V::lanes];
+    for (unsigned i = 0; i < V::lanes; i++) {
+        int32_t lane = -1;
+        if (!ToInt32(cx, args[i + 1], &lane))
+            return false;
+        if (lane < 0 || lane >= V::lanes)
+            return ErrorBadArgs(cx);
+        lanes[i] = lane;
+    }
+
+    Elem *val = TypedObjectMemory<Elem *>(args[0]);
 
     Elem result[V::lanes];
-    if (args.length() == 2) {
-        if (!IsVectorObject<V>(args[0]) || !args[1].isInt32())
-            return ErrorBadArgs(cx);
+    for (unsigned i = 0; i < V::lanes; i++)
+        result[i] = val[lanes[i]];
+
+    return StoreResult<V>(cx, args, result);
+}
 
-        Elem *val = TypedObjectMemory<Elem *>(args[0]);
-        int32_t maskArg;
-        if (!ToInt32(cx, args[1], &maskArg))
-            return false;
-        if (maskArg < 0 || maskArg > MAX_MASK_VALUE)
-            return ErrorBadArgs(cx);
+template<typename V>
+static bool
+Shuffle(JSContext *cx, unsigned argc, Value *vp)
+{
+    typedef typename V::Elem Elem;
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != (V::lanes + 2) || !IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]))
+        return ErrorBadArgs(cx);
 
-        for (unsigned i = 0; i < V::lanes; i++)
-            result[i] = val[(maskArg >> (i * SELECT_SHIFT)) & SELECT_MASK];
-    } else {
-        MOZ_ASSERT(args.length() == 3);
-        if (!IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]) || !args[2].isInt32())
+    int32_t lanes[V::lanes];
+    for (unsigned i = 0; i < V::lanes; i++) {
+        int32_t lane = -1;
+        if (!ToInt32(cx, args[i + 2], &lane))
+            return false;
+        if (lane < 0 || lane >= (2 * V::lanes))
             return ErrorBadArgs(cx);
+        lanes[i] = lane;
+    }
 
-        Elem *val1 = TypedObjectMemory<Elem *>(args[0]);
-        Elem *val2 = TypedObjectMemory<Elem *>(args[1]);
-        int32_t maskArg;
-        if (!ToInt32(cx, args[2], &maskArg))
-            return false;
-        if (maskArg < 0 || maskArg > MAX_MASK_VALUE)
-            return ErrorBadArgs(cx);
+    Elem *lhs = TypedObjectMemory<Elem *>(args[0]);
+    Elem *rhs = TypedObjectMemory<Elem *>(args[1]);
 
-        unsigned i = 0;
-        for (; i < V::lanes / 2; i++)
-            result[i] = val1[(maskArg >> (i * SELECT_SHIFT)) & SELECT_MASK];
-        for (; i < V::lanes; i++)
-            result[i] = val2[(maskArg >> (i * SELECT_SHIFT)) & SELECT_MASK];
+    Elem result[V::lanes];
+    for (unsigned i = 0; i < V::lanes; i++) {
+        Elem *selectedInput = lanes[i] < V::lanes ? lhs : rhs;
+        result[i] = selectedInput[lanes[i] % V::lanes];
     }
 
     return StoreResult<V>(cx, args, result);
 }
 
 template<typename Op>
 static bool
 Int32x4BinaryScalar(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -40,36 +40,39 @@
   V(greaterThan, (BinaryFunc<Float32x4, GreaterThan, Int32x4>), 2, 0)               \
   V(greaterThanOrEqual, (BinaryFunc<Float32x4, GreaterThanOrEqual, Int32x4>), 2, 0) \
   V(lessThan, (BinaryFunc<Float32x4, LessThan, Int32x4>), 2, 0)                     \
   V(lessThanOrEqual, (BinaryFunc<Float32x4, LessThanOrEqual, Int32x4>), 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, (BinaryFunc<Float32x4, NotEqual, Int32x4>), 2, 0)                     \
-  V(shuffle, FuncShuffle<Float32x4>, 2, 0)                                          \
   V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0)               \
   V(scale, (FuncWith<Float32x4, Scale>), 2, 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)                                          \
   V(clamp, Float32x4Clamp, 3, 0)                                                    \
-  V(select, Float32x4Select, 3, 0)                                                  \
-  V(shuffleMix, FuncShuffle<Float32x4>, 3, 0)
+  V(select, Float32x4Select, 3, 0)
+
+#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)                                          \
+  V(swizzle, Swizzle<Float32x4>, 2, 0)                                              \
+  V(shuffle, Shuffle<Float32x4>, 3, 0)
 
 #define FLOAT32X4_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_NULLARY_FUNCTION_LIST(V)                                                \
   FLOAT32X4_UNARY_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_BINARY_FUNCTION_LIST(V)                                                 \
-  FLOAT32X4_TERNARY_FUNCTION_LIST(V)
+  FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                                \
+  FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
 
 #define INT32X4_NULLARY_FUNCTION_LIST(V)                                            \
   V(zero, (FuncZero<Int32x4>), 0, 0)
 
 #define INT32X4_UNARY_FUNCTION_LIST(V)                                              \
   V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0)                         \
   V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0)                 \
   V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1, 0)                                  \
@@ -83,40 +86,43 @@
   V(greaterThan, (BinaryFunc<Int32x4, GreaterThan, Int32x4>), 2, 0)                 \
   V(lessThan, (BinaryFunc<Int32x4, LessThan, Int32x4>), 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(shuffle, FuncShuffle<Int32x4>, 2, 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)                                        \
   V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2, 0)
 
 #define INT32X4_TERNARY_FUNCTION_LIST(V)                                            \
   V(select, Int32x4Select, 3, 0)                                                    \
-  V(shuffleMix, FuncShuffle<Int32x4>, 3, 0)
 
 #define INT32X4_QUARTERNARY_FUNCTION_LIST(V)                                        \
   V(bool, Int32x4Bool, 4, 0)
 
+#define INT32X4_SHUFFLE_FUNCTION_LIST(V)                                            \
+  V(swizzle, Swizzle<Int32x4>, 2, 0)                                                \
+  V(shuffle, Shuffle<Int32x4>, 3, 0)
+
 #define INT32X4_FUNCTION_LIST(V)                                                    \
   INT32X4_NULLARY_FUNCTION_LIST(V)                                                  \
   INT32X4_UNARY_FUNCTION_LIST(V)                                                    \
   INT32X4_BINARY_FUNCTION_LIST(V)                                                   \
   INT32X4_TERNARY_FUNCTION_LIST(V)                                                  \
-  INT32X4_QUARTERNARY_FUNCTION_LIST(V)
+  INT32X4_QUARTERNARY_FUNCTION_LIST(V)                                              \
+  INT32X4_SHUFFLE_FUNCTION_LIST(V)
 
 #define FOREACH_INT32X4_SIMD_OP(_)   \
     _(fromFloat32x4)                 \
     _(fromFloat32x4Bits)             \
     _(shiftLeft)                     \
     _(shiftRight)                    \
     _(shiftRightLogical)
 #define FOREACH_FLOAT32X4_SIMD_OP(_) \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -416,17 +416,17 @@ MSG_DEF(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_
 MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
-MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_ERR, "invalid arguments")
+MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,   0, JSEXN_ERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_ERR, "argument {0} must be >= 0")
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
 
 // Shared array buffer
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH,  0, JSEXN_RANGEERR, "length argument out of range")
deleted file mode 100644
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4shuffle.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'float32x4 shuffle';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
-  var a = float32x4(1, 2, 3, 4);
-  var c = SIMD.float32x4.shuffle(a, 0x1B);
-  assertEq(c.x, 4);
-  assertEq(c.y, 3);
-  assertEq(c.z, 2);
-  assertEq(c.w, 1);
-
-  var d = SIMD.float32x4.shuffle(a, 0xFF);
-  assertEq(d.x, 4);
-  assertEq(d.y, 4);
-  assertEq(d.z, 4);
-  assertEq(d.w, 4);
-
-  var d = SIMD.float32x4.shuffle(a, 0x0);
-  assertEq(d.x, 1);
-  assertEq(d.y, 1);
-  assertEq(d.z, 1);
-  assertEq(d.w, 1);
-
-  var caught = false;
-  try {
-      var _ = SIMD.float32x4.shuffle(a, 0xFF + 1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be more than 0xFF");
-
-  var caught = false;
-  try {
-      var _ = SIMD.float32x4.shuffle(a, -1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be less than 0");
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
deleted file mode 100644
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4shufflemix.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'float32x4 shuffleMix';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
-  var a = float32x4(1, 2, 3, 4);
-  var b = float32x4(10, 20, 30, 40);
-  var c = SIMD.float32x4.shuffleMix(a,b, 0x1B);
-  assertEq(c.x, 4);
-  assertEq(c.y, 3);
-  assertEq(c.z, 20);
-  assertEq(c.w, 10);
-
-  var d = SIMD.float32x4.shuffleMix(a, b, 0xFF);
-  assertEq(d.x, 4);
-  assertEq(d.y, 4);
-  assertEq(d.z, 40);
-  assertEq(d.w, 40);
-
-  var d = SIMD.float32x4.shuffleMix(a, b, 0x0);
-  assertEq(d.x, 1);
-  assertEq(d.y, 1);
-  assertEq(d.z, 10);
-  assertEq(d.w, 10);
-
-  var caught = false;
-  try {
-      var _ = SIMD.float32x4.shuffleMix(a, b, 0xFF + 1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be more than 0xFF");
-
-  var caught = false;
-  try {
-      var _ = SIMD.float32x4.shuffleMix(a, b, -1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be less than 0");
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
deleted file mode 100644
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'int32x4 shuffle';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  var a = int32x4(1, 2, 3, 4);
-  var c = SIMD.int32x4.shuffle(a, 0x1B);
-  assertEq(c.x, 4);
-  assertEq(c.y, 3);
-  assertEq(c.z, 2);
-  assertEq(c.w, 1);
-
-  var d = SIMD.int32x4.shuffle(a, 0xFF);
-  assertEq(d.x, 4);
-  assertEq(d.y, 4);
-  assertEq(d.z, 4);
-  assertEq(d.w, 4);
-
-  var d = SIMD.int32x4.shuffle(a, 0x0);
-  assertEq(d.x, 1);
-  assertEq(d.y, 1);
-  assertEq(d.z, 1);
-  assertEq(d.w, 1);
-
-  var caught = false;
-  try {
-      var _ = SIMD.int32x4.shuffle(a, b, 0xFF + 1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be more than 0xFF");
-
-  var caught = false;
-  try {
-      var _ = SIMD.int32x4.shuffle(a, b, -1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be less than 0");
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
deleted file mode 100644
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'int32x4 shuffleMix';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  var a = int32x4(1, 2, 3, 4);
-  var b = int32x4(10, 20, 30, 40);
-  var c = SIMD.int32x4.shuffleMix(a,b, 0x1B);
-  assertEq(c.x, 4);
-  assertEq(c.y, 3);
-  assertEq(c.z, 20);
-  assertEq(c.w, 10);
-
-  var d = SIMD.int32x4.shuffleMix(a, b, 0xFF);
-  assertEq(d.x, 4);
-  assertEq(d.y, 4);
-  assertEq(d.z, 40);
-  assertEq(d.w, 40);
-
-  var d = SIMD.int32x4.shuffleMix(a, b, 0x0);
-  assertEq(d.x, 1);
-  assertEq(d.y, 1);
-  assertEq(d.z, 10);
-  assertEq(d.w, 10);
-
-  var caught = false;
-  try {
-      var _ = SIMD.int32x4.shuffleMix(a, b, 0xFF + 1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be more than 0xFF");
-
-  var caught = false;
-  try {
-      var _ = SIMD.int32x4.shuffleMix(a, b, -1);
-  } catch (e) {
-      caught = true;
-  }
-  assertEq(caught, true, "Mask can't be less than 0");
-
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/simd/swizzle-shuffle.js
@@ -0,0 +1,149 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+/*
+ * 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]];
+}
+
+function testSwizzleForType(type) {
+    var v = type(1,2,3,4);
+
+    assertThrowsInstanceOf(() => type.swizzle()               , TypeError);
+    assertThrowsInstanceOf(() => type.swizzle(v, 0)           , TypeError);
+    assertThrowsInstanceOf(() => type.swizzle(v, 0, 1)        , TypeError);
+    assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2)     , TypeError);
+    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));
+    }
+
+    // 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));
+
+    // Object for which ToInt32 will fail.
+    obj = {
+        valueOf: function() { throw new Error; }
+    };
+    assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, obj), Error);
+}
+
+function testSwizzleInt32x4() {
+    var v = int32x4(1, 2, 3, 4);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.swizzle(v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testSwizzleForType(int32x4);
+}
+
+function testSwizzleFloat32x4() {
+    var v = float32x4(1, 2, 3, 4);
+
+    assertThrowsInstanceOf(function() {
+        int32x4.swizzle(v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testSwizzleForType(float32x4);
+}
+
+function shuffle(lhsa, rhsa, x, y, z, w) {
+    return [(x < 4 ? lhsa : rhsa)[x % 4],
+            (y < 4 ? lhsa : rhsa)[y % 4],
+            (z < 4 ? lhsa : rhsa)[z % 4],
+            (w < 4 ? lhsa : rhsa)[w % 4]];
+}
+
+function testShuffleForType(type) {
+    var lhs = type(1,2,3,4);
+    var rhs = type(5,6,7,8);
+
+    assertThrowsInstanceOf(() => type.shuffle(lhs)                   , TypeError);
+    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs)              , TypeError);
+    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0)           , TypeError);
+    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1)        , TypeError);
+    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2)     , TypeError);
+    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));
+    }
+
+    // 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));
+
+    // Object for which ToInt32 will fail.
+    obj = {
+        valueOf: function() { throw new Error; }
+    };
+    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, obj), Error);
+}
+
+function testShuffleInt32x4() {
+    var v = int32x4(1, 2, 3, 4);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.shuffle(v, v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testShuffleForType(int32x4);
+}
+
+function testShuffleFloat32x4() {
+    var v = float32x4(1, 2, 3, 4);
+
+    assertThrowsInstanceOf(function() {
+        int32x4.shuffle(v, v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testShuffleForType(float32x4);
+}
+
+testSwizzleInt32x4();
+testSwizzleFloat32x4();
+testShuffleInt32x4();
+testShuffleFloat32x4();
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);