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 id27667
push usercbook@mozilla.com
push dateMon, 20 Oct 2014 12:40:56 +0000
treeherdermozilla-central@cc2d8bdbccb8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1083238
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 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);