Bug 1160971 - Part 5: ASM.js boolean vectors. r=bbouvier
authorJakob Stoklund Olesen <jolesen@mozilla.com>
Tue, 22 Dec 2015 14:17:13 -0800
changeset 277432 6d42293cc7917c59b2953de56a654a6a69d94867
parent 277431 739b78a0d10987c77e2b5a53b83910e53dcd48c7
child 277433 862b3306d68a5eae87ce492fdff26f75ea9a4adc
push id29822
push usercbook@mozilla.com
push dateWed, 23 Dec 2015 11:00:24 +0000
treeherdermozilla-central@35b211eaad1f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1160971
milestone46.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 1160971 - Part 5: ASM.js boolean vectors. r=bbouvier Implement asm.js support for Bool32x4. Also remove asm.js tests for the now removed bitwise operations on Float32x4.
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/asmjs/Wasm.h
js/src/asmjs/WasmIR.h
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmStubs.cpp
js/src/jit-test/tests/asm.js/simd-fbirds.js
js/src/jit-test/tests/asm.js/simd-mandelbrot.js
js/src/jit-test/tests/asm.js/testSIMD.js
js/src/jit-test/tests/asm.js/testZOOB.js
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -100,17 +100,17 @@ GetDataProperty(JSContext* cx, HandleVal
 
     v.set(desc.value());
     return true;
 }
 
 static bool
 HasPureCoercion(JSContext* cx, HandleValue v)
 {
-    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v))
+    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v))
         return true;
 
     // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
     // bug that generates code that passes functions for some imports. To avoid
     // breaking all the code that contains this bug, we make an exception for
     // functions that don't have user-defined valueOf or toString, for their
     // coercions are not observable and coercion via ToNumber/ToInt32
     // definitely produces NaN/0. We should remove this special case later once
@@ -143,16 +143,18 @@ ValidateGlobalVariable(JSContext* cx, co
             MOZ_CRASH("int64");
           case ValType::F32:
             *(float*)datum = v.f32();
             break;
           case ValType::F64:
             *(double*)datum = v.f64();
             break;
           case ValType::I32x4:
+          case ValType::B32x4:
+            // Bool32x4 uses the same data layout as Int32x4.
             memcpy(datum, v.i32x4(), Simd128DataSize);
             break;
           case ValType::F32x4:
             memcpy(datum, v.f32x4(), Simd128DataSize);
             break;
         }
         break;
       }
@@ -190,16 +192,24 @@ ValidateGlobalVariable(JSContext* cx, co
           }
           case ValType::F32x4: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
                 return false;
             memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
             break;
           }
+          case ValType::B32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
+                return false;
+            // Bool32x4 uses the same data layout as Int32x4.
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          }
         }
         break;
       }
     }
 
     return true;
 }
 
@@ -302,26 +312,28 @@ ValidateMathBuiltinFunction(JSContext* c
 }
 
 static PropertyName*
 SimdTypeToName(JSContext* cx, AsmJSSimdType type)
 {
     switch (type) {
       case AsmJSSimdType_int32x4:   return cx->names().int32x4;
       case AsmJSSimdType_float32x4: return cx->names().float32x4;
+      case AsmJSSimdType_bool32x4:  return cx->names().bool32x4;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
 }
 
 static SimdTypeDescr::Type
 AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
 {
     switch (type) {
       case AsmJSSimdType_int32x4: return Int32x4::type;
       case AsmJSSimdType_float32x4: return Float32x4::type;
+      case AsmJSSimdType_bool32x4: return Bool32x4::type;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
 }
 
 static bool
 ValidateSimdType(JSContext* cx, AsmJSModule::Global& global, HandleValue globalVal,
                  MutableHandleValue out)
 {
@@ -371,16 +383,17 @@ ValidateSimdOperation(JSContext* cx, Asm
     RootedPropertyName opName(cx, global.simdOperationName());
     if (!GetDataProperty(cx, v, opName, &v))
         return false;
 
     Native native = nullptr;
     switch (global.simdOperationType()) {
 #define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
 #define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
+#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break;
 #define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
       case AsmJSSimdType_int32x4:
         switch (global.simdOperation()) {
           FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
           default:
             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
                                                     "place");
         }
@@ -388,19 +401,28 @@ ValidateSimdOperation(JSContext* cx, Asm
       case AsmJSSimdType_float32x4:
         switch (global.simdOperation()) {
           FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)
           default:
              MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
                                                      "place");
         }
         break;
+      case AsmJSSimdType_bool32x4:
+        switch (global.simdOperation()) {
+          FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4)
+          default:
+             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                     "place");
+        }
+        break;
 #undef FALLTHROUGH
 #undef SET_NATIVE_FLOAT32X4
 #undef SET_NATIVE_INT32X4
+#undef SET_NATIVE_BOOL32X4
 #undef SET_NATIVE
     }
     if (!native || !IsNativeFunction(v, native))
         return LinkFail(cx, "bad SIMD.type.* operation");
     return true;
 }
 
 static bool
@@ -712,16 +734,24 @@ CallAsmJS(JSContext* cx, unsigned argc, 
           }
           case ValType::F32x4: {
             SimdConstant simd;
             if (!ToSimdConstant<Float32x4>(cx, v, &simd))
                 return false;
             memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
             break;
           }
+          case ValType::B32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Bool32x4>(cx, v, &simd))
+                return false;
+            // Bool32x4 uses the same representation as Int32x4.
+            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
+            break;
+          }
         }
     }
 
     // The correct way to handle this situation would be to allocate a new range
     // of PROT_NONE memory and module.changeHeap to this memory. That would
     // cause every access to take the out-of-bounds signal-handler path which
     // does the right thing. For now, just throw an out-of-memory exception
     // since these can technically pop out anywhere and the full fix may
@@ -779,16 +809,22 @@ CallAsmJS(JSContext* cx, unsigned argc, 
         callArgs.rval().set(ObjectValue(*simdObj));
         break;
       case ExprType::F32x4:
         simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
         if (!simdObj)
             return false;
         callArgs.rval().set(ObjectValue(*simdObj));
         break;
+      case ExprType::B32x4:
+        simdObj = CreateSimd<Bool32x4>(cx, (int32_t*)&coercedArgs[0]);
+        if (!simdObj)
+            return false;
+        callArgs.rval().set(ObjectValue(*simdObj));
+        break;
     }
 
     return true;
 }
 
 static JSFunction*
 NewExportedFunction(JSContext* cx, const AsmJSModule::ExportedFunction& func,
                     HandleObject moduleObj, unsigned exportIndex)
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -68,17 +68,18 @@ enum AsmJSAtomicsBuiltinFunction
     AsmJSAtomicsBuiltin_xor,
     AsmJSAtomicsBuiltin_isLockFree
 };
 
 // Set of known global object SIMD's attributes, i.e. types
 enum AsmJSSimdType
 {
     AsmJSSimdType_int32x4,
-    AsmJSSimdType_float32x4
+    AsmJSSimdType_float32x4,
+    AsmJSSimdType_bool32x4
 };
 
 // Set of known operations, for a given SIMD type (int32x4, float32x4,...)
 enum AsmJSSimdOperation
 {
 #define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
     FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION)
 #undef ASMJSSIMDOPERATION
@@ -866,19 +867,29 @@ class AsmJSModule
         *globalDataOffset = pod.globalBytes_;
         pod.globalBytes_ += bytes;
         return true;
     }
     bool addGlobalVar(wasm::ValType type, uint32_t* globalDataOffset) {
         MOZ_ASSERT(!isFinished());
         unsigned width = 0;
         switch (type) {
-          case wasm::ValType::I32:   case wasm::ValType::F32:   width = 4;  break;
-          case wasm::ValType::I64:   case wasm::ValType::F64:   width = 8;  break;
-          case wasm::ValType::I32x4: case wasm::ValType::F32x4: width = 16; break;
+          case wasm::ValType::I32:
+          case wasm::ValType::F32:
+            width = 4;
+            break;
+          case wasm::ValType::I64:
+          case wasm::ValType::F64:
+            width = 8;
+            break;
+          case wasm::ValType::I32x4:
+          case wasm::ValType::F32x4:
+          case wasm::ValType::B32x4:
+            width = 16;
+            break;
         }
         return allocateGlobalBytes(width, width, globalDataOffset);
     }
   public:
     bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalDataOffset) {
         MOZ_ASSERT(!isFinished());
         if (!addGlobalVar(v.type(), globalDataOffset))
             return false;
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -527,16 +527,17 @@ class NumLit
     enum Which {
         Fixnum,
         NegativeInt,
         BigUnsigned,
         Double,
         Float,
         Int32x4,
         Float32x4,
+        Bool32x4,
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
     union {
         Value scalar_;
         jit::SimdConstant simd_;
@@ -579,17 +580,17 @@ class NumLit
     }
 
     Value scalarValue() const {
         MOZ_ASSERT(which_ != OutOfRangeInt);
         return u.scalar_;
     }
 
     bool isSimd() const {
-        return which_ == Int32x4 || which_ == Float32x4;
+        return which_ == Int32x4 || which_ == Float32x4 || which_ == Bool32x4;
     }
 
     const jit::SimdConstant& simdValue() const {
         MOZ_ASSERT(isSimd());
         return u.simd_;
     }
 
     bool valid() const {
@@ -605,16 +606,18 @@ class NumLit
           case NumLit::Double:
             return ValType::F64;
           case NumLit::Float:
             return ValType::F32;
           case NumLit::Int32x4:
             return ValType::I32x4;
           case NumLit::Float32x4:
             return ValType::F32x4;
+          case NumLit::Bool32x4:
+            return ValType::B32x4;
           case NumLit::OutOfRangeInt:;
         }
         MOZ_CRASH("bad literal");
     }
 
     Val value() const {
         switch (which_) {
           case NumLit::Fixnum:
@@ -624,16 +627,18 @@ class NumLit
           case NumLit::Float:
             return Val(toFloat());
           case NumLit::Double:
             return Val(toDouble());
           case NumLit::Int32x4:
             return Val(simdValue().asInt32x4());
           case NumLit::Float32x4:
             return Val(simdValue().asFloat32x4());
+          case NumLit::Bool32x4:
+            return Val(simdValue().asInt32x4(), ValType::B32x4);
           case NumLit::OutOfRangeInt:;
         }
         MOZ_CRASH("bad literal");
     }
 };
 
 // Respresents the type of a general asm.js expression.
 class Type
@@ -642,16 +647,17 @@ class Type
     enum Which {
         Fixnum = NumLit::Fixnum,
         Signed = NumLit::NegativeInt,
         Unsigned = NumLit::BigUnsigned,
         DoubleLit = NumLit::Double,
         Float = NumLit::Float,
         Int32x4 = NumLit::Int32x4,
         Float32x4 = NumLit::Float32x4,
+        Bool32x4 = NumLit::Bool32x4,
         Double,
         MaybeDouble,
         MaybeFloat,
         Floatish,
         Int,
         Intish,
         Void
     };
@@ -665,49 +671,54 @@ class Type
     MOZ_IMPLICIT Type(AsmJSSimdType type) {
         switch (type) {
           case AsmJSSimdType_int32x4:
             which_ = Int32x4;
             return;
           case AsmJSSimdType_float32x4:
             which_ = Float32x4;
             return;
+          case AsmJSSimdType_bool32x4:
+            which_ = Bool32x4;
+            return;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad AsmJSSimdType");
     }
 
     static Type var(ValType t) {
         switch (t) {
           case ValType::I32:   return Int;
           case ValType::I64:   MOZ_CRASH("no int64 in asm.js");
           case ValType::F32:   return Float;
           case ValType::F64:   return Double;
           case ValType::I32x4: return Int32x4;
           case ValType::F32x4: return Float32x4;
+          case ValType::B32x4: return Bool32x4;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
     }
 
     static Type ret(ExprType t) {
         switch (t) {
           case ExprType::Void:   return Type::Void;
           case ExprType::I32:    return Signed;
           case ExprType::I64:    MOZ_CRASH("no int64 in asm.js");
           case ExprType::F32:    return Float;
           case ExprType::F64:    return Double;
           case ExprType::I32x4:  return Int32x4;
           case ExprType::F32x4:  return Float32x4;
+          case ExprType::B32x4:  return Bool32x4;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
     }
 
     static Type lit(const NumLit& lit) {
         MOZ_ASSERT(lit.valid());
         Which which = Type::Which(lit.which());
-        MOZ_ASSERT(which >= Fixnum && which <= Float32x4);
+        MOZ_ASSERT(which >= Fixnum && which <= Bool32x4);
         Type t;
         t.which_ = which;
         return t;
     }
 
     Which which() const { return which_; }
 
     bool operator==(Type rhs) const { return which_ == rhs.which_; }
@@ -717,16 +728,17 @@ class Type
         switch (rhs.which_) {
           case Signed:      return isSigned();
           case Unsigned:    return isUnsigned();
           case DoubleLit:   return isDoubleLit();
           case Double:      return isDouble();
           case Float:       return isFloat();
           case Int32x4:     return isInt32x4();
           case Float32x4:   return isFloat32x4();
+          case Bool32x4:    return isBool32x4();
           case MaybeDouble: return isMaybeDouble();
           case MaybeFloat:  return isMaybeFloat();
           case Floatish:    return isFloatish();
           case Int:         return isInt();
           case Intish:      return isIntish();
           case Fixnum:      return isFixnum();
           case Void:        return isVoid();
         }
@@ -736,16 +748,17 @@ class Type
     bool operator<=(ValType rhs) const {
         switch (rhs) {
           case ValType::I32:    return isInt();
           case ValType::I64:    MOZ_CRASH("no int64 in asm.js");
           case ValType::F32:    return isFloat();
           case ValType::F64:    return isDouble();
           case ValType::I32x4:  return isInt32x4();
           case ValType::F32x4:  return isFloat32x4();
+          case ValType::B32x4:  return isBool32x4();
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected rhs type");
     }
 
     bool isFixnum() const {
         return which_ == Fixnum;
     }
 
@@ -800,18 +813,22 @@ class Type
     bool isInt32x4() const {
         return which_ == Int32x4;
     }
 
     bool isFloat32x4() const {
         return which_ == Float32x4;
     }
 
+    bool isBool32x4() const {
+        return which_ == Bool32x4;
+    }
+
     bool isSimd() const {
-        return isInt32x4() || isFloat32x4();
+        return isInt32x4() || isFloat32x4() || isBool32x4();
     }
 
     bool isVarType() const {
         return isInt() || isFloat() || isDouble() || isSimd();
     }
 
     ValType checkedValueType() const {
         MOZ_ASSERT(isVarType());
@@ -841,29 +858,33 @@ class Type
           case Signed:
           case Unsigned:
           case Intish:
             return jit::MIRType_Int32;
           case Int32x4:
             return jit::MIRType_Int32x4;
           case Float32x4:
             return jit::MIRType_Float32x4;
+          case Bool32x4:
+            return jit::MIRType_Bool32x4;
           case Void:
             return jit::MIRType_None;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
     }
 
     AsmJSSimdType simdType() const {
         MOZ_ASSERT(isSimd());
         switch (which_) {
           case Int32x4:
             return AsmJSSimdType_int32x4;
           case Float32x4:
             return AsmJSSimdType_float32x4;
+          case Bool32x4:
+            return AsmJSSimdType_bool32x4;
           // Scalar types
           case Double:
           case DoubleLit:
           case MaybeDouble:
           case Float:
           case MaybeFloat:
           case Floatish:
           case Fixnum:
@@ -887,16 +908,17 @@ class Type
           case MaybeFloat:  return "float?";
           case Fixnum:      return "fixnum";
           case Int:         return "int";
           case Signed:      return "signed";
           case Unsigned:    return "unsigned";
           case Intish:      return "intish";
           case Int32x4:     return "int32x4";
           case Float32x4:   return "float32x4";
+          case Bool32x4:    return "bool32x4";
           case Void:        return "void";
         }
         MOZ_CRASH("Invalid Type");
     }
 };
 
 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 
@@ -1786,16 +1808,19 @@ IsCoercionCall(ModuleValidator& m, Parse
     if (global->isSimdOperation() && global->simdOperation() == AsmJSSimdOperation_check) {
         switch (global->simdOperationType()) {
           case AsmJSSimdType_int32x4:
             *coerceTo = ValType::I32x4;
             return true;
           case AsmJSSimdType_float32x4:
             *coerceTo = ValType::F32x4;
             return true;
+          case AsmJSSimdType_bool32x4:
+            *coerceTo = ValType::B32x4;
+            return true;
         }
     }
 
     return false;
 }
 
 static bool
 IsFloatLiteral(ModuleValidator& m, ParseNode* pn)
@@ -1809,18 +1834,19 @@ IsFloatLiteral(ModuleValidator& m, Parse
         return false;
     return IsNumericNonFloatLiteral(coercedExpr);
 }
 
 static unsigned
 SimdTypeToLength(AsmJSSimdType type)
 {
     switch (type) {
+      case AsmJSSimdType_int32x4:
       case AsmJSSimdType_float32x4:
-      case AsmJSSimdType_int32x4:
+      case AsmJSSimdType_bool32x4:
         return 4;
     }
     MOZ_CRASH("unexpected SIMD type");
 }
 
 static bool
 IsSimdTuple(ModuleValidator& m, ParseNode* pn, AsmJSSimdType* type)
 {
@@ -1858,16 +1884,17 @@ IsSimdLiteral(ModuleValidator& m, ParseN
     unsigned length = SimdTypeToLength(type);
     for (unsigned i = 0; i < length; i++) {
         if (!IsNumericLiteral(m, arg))
             return false;
 
         uint32_t _;
         switch (type) {
           case AsmJSSimdType_int32x4:
+          case AsmJSSimdType_bool32x4:
             if (!IsLiteralInt(m, arg, &_))
                 return false;
           case AsmJSSimdType_float32x4:
             if (!IsNumericNonFloatLiteral(arg))
                 return false;
         }
 
         arg = NextNode(arg);
@@ -1928,16 +1955,27 @@ ExtractSimdValue(ModuleValidator& m, Par
       case AsmJSSimdType_float32x4: {
         MOZ_ASSERT(SimdTypeToLength(type) == 4);
         float val[4];
         for (size_t i = 0; i < 4; i++, arg = NextNode(arg))
             val[i] = float(ExtractNumericNonFloatValue(arg));
         MOZ_ASSERT(arg == nullptr);
         return NumLit(NumLit::Float32x4, SimdConstant::CreateX4(val));
       }
+      case AsmJSSimdType_bool32x4: {
+        MOZ_ASSERT(SimdTypeToLength(type) == 4);
+        int32_t val[4];
+        for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = u32 ? -1 : 0;
+        }
+        MOZ_ASSERT(arg == nullptr);
+        return NumLit(NumLit::Bool32x4, SimdConstant::CreateX4(val));
+      }
     }
 
     MOZ_CRASH("Unexpected SIMD type.");
 }
 
 static NumLit
 ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn)
 {
@@ -1996,16 +2034,17 @@ IsLiteralInt(NumLit lit, uint32_t* u32)
       case NumLit::NegativeInt:
         *u32 = lit.toUint32();
         return true;
       case NumLit::Double:
       case NumLit::Float:
       case NumLit::OutOfRangeInt:
       case NumLit::Int32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool32x4:
         return false;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
 }
 
 static inline bool
 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32)
 {
@@ -2221,16 +2260,21 @@ class MOZ_STACK_CLASS FunctionValidator
           case NumLit::Int32x4:
             writeOp(I32X4::Literal);
             funcIR().writeI32X4(lit.simdValue().asInt32x4());
             return;
           case NumLit::Float32x4:
             writeOp(F32X4::Literal);
             funcIR().writeF32X4(lit.simdValue().asFloat32x4());
             return;
+          case NumLit::Bool32x4:
+            // Boolean vectors use the Int32x4 memory representation.
+            writeOp(B32X4::Literal);
+            funcIR().writeI32X4(lit.simdValue().asInt32x4());
+            return;
           case NumLit::OutOfRangeInt:
             break;
         }
         MOZ_CRASH("unexpected literal type");
     }
 
     template<class T>
     void patchOp(size_t pos, T stmt) {
@@ -2548,16 +2592,20 @@ IsSimdTypeName(ModuleValidator& m, Prope
     if (name == m.cx()->names().int32x4) {
         *type = AsmJSSimdType_int32x4;
         return true;
     }
     if (name == m.cx()->names().float32x4) {
         *type = AsmJSSimdType_float32x4;
         return true;
     }
+    if (name == m.cx()->names().bool32x4) {
+        *type = AsmJSSimdType_bool32x4;
+        return true;
+    }
     return false;
 }
 
 static bool
 IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
 {
 #define CASE(op) case AsmJSSimdOperation_##op:
     switch(type) {
@@ -2568,16 +2616,22 @@ IsSimdValidOperationType(AsmJSSimdType t
         }
         break;
       case AsmJSSimdType_float32x4:
         switch (op) {
           FORALL_FLOAT32X4_ASMJS_OP(CASE) return true;
           default: return false;
         }
         break;
+      case AsmJSSimdType_bool32x4:
+        switch (op) {
+          FORALL_BOOL_SIMD_OP(CASE) return true;
+          default: return false;
+        }
+        break;
     }
 #undef CASE
     MOZ_CRASH("Unhandles SIMD type");
 }
 
 static bool
 CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
                       PropertyName* field)
@@ -2945,16 +2999,17 @@ CheckVarRef(FunctionValidator& f, ParseN
     if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
         switch (local->type) {
           case ValType::I32:    f.writeOp(I32::GetLocal);   break;
           case ValType::I64:    MOZ_CRASH("no int64 in asm.js");
           case ValType::F32:    f.writeOp(F32::GetLocal);   break;
           case ValType::F64:    f.writeOp(F64::GetLocal);   break;
           case ValType::I32x4:  f.writeOp(I32X4::GetLocal); break;
           case ValType::F32x4:  f.writeOp(F32X4::GetLocal); break;
+          case ValType::B32x4:  f.writeOp(B32X4::GetLocal); break;
         }
         f.writeU32(local->slot);
         *type = Type::var(local->type);
         return true;
     }
 
     if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
         switch (global->which()) {
@@ -2965,16 +3020,17 @@ CheckVarRef(FunctionValidator& f, ParseN
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable: {
             switch (global->varOrConstType().which()) {
               case Type::Int:       f.writeOp(I32::GetGlobal);   break;
               case Type::Double:    f.writeOp(F64::GetGlobal);   break;
               case Type::Float:     f.writeOp(F32::GetGlobal);   break;
               case Type::Int32x4:   f.writeOp(I32X4::GetGlobal); break;
               case Type::Float32x4: f.writeOp(F32X4::GetGlobal); break;
+              case Type::Bool32x4:  f.writeOp(B32X4::GetGlobal); break;
               default: MOZ_CRASH("unexpected global type");
             }
 
             f.writeU32(global->varOrConstGlobalDataOffset());
             f.writeU8(uint8_t(global->isConst()));
             *type = global->varOrConstType();
             break;
           }
@@ -3298,16 +3354,17 @@ CheckAssignName(FunctionValidator& f, Pa
 
         switch (lhsVar->type) {
           case ValType::I32:    f.patchOp(opcodeAt, I32::SetLocal);   break;
           case ValType::I64:    MOZ_CRASH("no int64 in asm.js");
           case ValType::F64:    f.patchOp(opcodeAt, F64::SetLocal);   break;
           case ValType::F32:    f.patchOp(opcodeAt, F32::SetLocal);   break;
           case ValType::I32x4:  f.patchOp(opcodeAt, I32X4::SetLocal); break;
           case ValType::F32x4:  f.patchOp(opcodeAt, F32X4::SetLocal); break;
+          case ValType::B32x4:  f.patchOp(opcodeAt, B32X4::SetLocal); break;
         }
 
         f.patch32(indexAt, lhsVar->slot);
         *type = rhsType;
         return true;
     }
 
     if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
@@ -3320,16 +3377,17 @@ CheckAssignName(FunctionValidator& f, Pa
         }
 
         switch (global->varOrConstType().which()) {
           case Type::Int:       f.patchOp(opcodeAt, I32::SetGlobal);   break;
           case Type::Float:     f.patchOp(opcodeAt, F32::SetGlobal);   break;
           case Type::Double:    f.patchOp(opcodeAt, F64::SetGlobal);   break;
           case Type::Int32x4:   f.patchOp(opcodeAt, I32X4::SetGlobal); break;
           case Type::Float32x4: f.patchOp(opcodeAt, F32X4::SetGlobal); break;
+          case Type::Bool32x4:  f.patchOp(opcodeAt, B32X4::SetGlobal); break;
           default: MOZ_CRASH("unexpected global type");
         }
 
         f.patch32(indexAt, global->varOrConstGlobalDataOffset());
         *type = rhsType;
         return true;
     }
 
@@ -3867,16 +3925,17 @@ CheckInternalCall(FunctionValidator& f, 
     switch (ret) {
       case ExprType::Void:   f.writeOp(Stmt::CallInternal);  break;
       case ExprType::I32:    f.writeOp(I32::CallInternal);   break;
       case ExprType::I64:    MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:    f.writeOp(F32::CallInternal);   break;
       case ExprType::F64:    f.writeOp(F64::CallInternal);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallInternal); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallInternal); break;
+      case ExprType::B32x4:  f.writeOp(B32X4::CallInternal); break;
     }
 
     // Function's index, to find out the function's entry
     size_t funcIndexAt = f.temp32();
     // Function's signature in lifo
     size_t sigAt = f.tempPtr();
     // Call node position (asm.js specific)
     WriteCallLineCol(f, callNode);
@@ -3961,16 +4020,17 @@ CheckFuncPtrCall(FunctionValidator& f, P
     switch (ret) {
       case ExprType::Void:   f.writeOp(Stmt::CallIndirect);  break;
       case ExprType::I32:    f.writeOp(I32::CallIndirect);   break;
       case ExprType::I64:    MOZ_CRASH("no in64 in asm.js");
       case ExprType::F32:    f.writeOp(F32::CallIndirect);   break;
       case ExprType::F64:    f.writeOp(F64::CallIndirect);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallIndirect); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallIndirect); break;
+      case ExprType::B32x4:  f.writeOp(B32X4::CallIndirect); break;
     }
 
     // Table's mask
     f.writeU32(mask);
     // Global data offset
     size_t globalDataOffsetAt = f.temp32();
     // Signature
     size_t sigAt = f.tempPtr();
@@ -4029,16 +4089,17 @@ CheckFFICall(FunctionValidator& f, Parse
     switch (ret) {
       case ExprType::Void:   f.writeOp(Stmt::CallImport);  break;
       case ExprType::I32:    f.writeOp(I32::CallImport);   break;
       case ExprType::I64:    MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:    f.writeOp(F32::CallImport);   break;
       case ExprType::F64:    f.writeOp(F64::CallImport);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallImport); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallImport); break;
+      case ExprType::B32x4:  f.writeOp(B32X4::CallImport); break;
     }
 
     // Global data offset
     size_t offsetAt = f.temp32();
     // Pointer to the exit's signature in the module's lifo
     size_t sigAt = f.tempPtr();
     // Call node position (asm.js specific)
     WriteCallLineCol(f, callNode);
@@ -4114,16 +4175,21 @@ CheckCoercionArg(FunctionValidator& f, P
             return f.fail(arg, "argument to SIMD int32x4 coercion isn't int32x4");
         f.patchOp(opcodeAt, I32X4::Id);
         break;
       case ValType::F32x4:
         if (!argType.isFloat32x4())
             return f.fail(arg, "argument to SIMD float32x4 coercion isn't float32x4");
         f.patchOp(opcodeAt, F32X4::Id);
         break;
+      case ValType::B32x4:
+        if (!argType.isBool32x4())
+            return f.fail(arg, "argument to SIMD bool32x4 coercion isn't bool32x4");
+        f.patchOp(opcodeAt, B32X4::Id);
+        break;
       case ValType::I32:
       case ValType::F64:
         MOZ_CRASH("not call coercions");
     }
 
     *type = Type::ret(ret);
     return true;
 }
@@ -4281,16 +4347,17 @@ class CheckArgIsSubtypeOf
     }
 };
 
 static inline Type
 SimdToCoercedScalarType(AsmJSSimdType t)
 {
     switch (t) {
       case AsmJSSimdType_int32x4:
+      case AsmJSSimdType_bool32x4:
         return Type::Intish;
       case AsmJSSimdType_float32x4:
         return Type::Floatish;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
 }
 
 class CheckSimdScalarArgs
@@ -4320,16 +4387,17 @@ class CheckSimdScalarArgs
             f.patchOp(patchAt, F32::FromF64);
             return true;
         }
 
         if (patchAt == size_t(-1))
             return true;
 
         switch (simdType_) {
+          case AsmJSSimdType_bool32x4:
           case AsmJSSimdType_int32x4:   f.patchOp(patchAt, I32::Id); return true;
           case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32::Id); return true;
         }
 
         MOZ_CRASH("unexpected simd type");
     }
 };
 
@@ -4338,19 +4406,19 @@ class CheckSimdSelectArgs
     Type formalType_;
 
   public:
     explicit CheckSimdSelectArgs(AsmJSSimdType t) : formalType_(t) {}
 
     bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
     {
         if (argIndex == 0) {
-            // First argument of select is an int32x4 mask.
-            if (!(actualType <= Type::Int32x4))
-                return f.failf(arg, "%s is not a subtype of Int32x4", actualType.toChars());
+            // First argument of select is a bool32x4 mask.
+            if (!(actualType <= Type::Bool32x4))
+                return f.failf(arg, "%s is not a subtype of Bool32x4", actualType.toChars());
             return true;
         }
 
         if (!(actualType <= formalType_)) {
             return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
                            formalType_.toChars());
         }
         return true;
@@ -4376,16 +4444,17 @@ class CheckSimdVectorScalarArgs
             }
 
             if (patchAt == size_t(-1))
                 return true;
 
             switch (formalSimdType_) {
               case AsmJSSimdType_int32x4:   f.patchOp(patchAt, I32X4::Id); return true;
               case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32X4::Id); return true;
+              case AsmJSSimdType_bool32x4:  f.patchOp(patchAt, B32X4::Id); return true;
             }
 
             MOZ_CRASH("unexpected simd type");
         }
 
         // Second argument is the scalar
         return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, patchAt);
     }
@@ -4437,16 +4506,17 @@ class CheckSimdReplaceLaneArgs
             // First argument is the vector
             if (!(actualType <= Type(formalSimdType_))) {
                 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
                                Type(formalSimdType_).toChars());
             }
             switch (formalSimdType_) {
               case AsmJSSimdType_int32x4:   f.patchOp(patchAt, I32X4::Id); break;
               case AsmJSSimdType_float32x4: f.patchOp(patchAt, F32X4::Id); break;
+              case AsmJSSimdType_bool32x4:  f.patchOp(patchAt, B32X4::Id); break;
             }
             return true;
           case 1:
             // Second argument is the lane (< vector length).
             if (!IsLiteralOrConstInt(f, arg, &u32))
                 return f.failf(arg, "lane selector should be a constant integer literal");
             if (u32 >= SimdTypeToLength(formalSimdType_))
                 return f.failf(arg, "lane selector should be in bounds");
@@ -4458,30 +4528,31 @@ class CheckSimdReplaceLaneArgs
         }
         return false;
     }
 };
 
 } // namespace
 
 static void
-SwitchPackOp(FunctionValidator& f, AsmJSSimdType type, I32X4 i32x4, F32X4 f32x4)
+SwitchPackOp(FunctionValidator& f, AsmJSSimdType type, I32X4 i32x4, F32X4 f32x4, B32X4 b32x4)
 {
     switch (type) {
       case AsmJSSimdType_int32x4:   f.writeOp(i32x4); return;
       case AsmJSSimdType_float32x4: f.writeOp(f32x4); return;
+      case AsmJSSimdType_bool32x4:  f.writeOp(b32x4); return;
     }
     MOZ_CRASH("unexpected simd type");
 }
 
 static bool
 CheckSimdUnary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                MSimdUnaryArith::Operation op, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::Unary, F32X4::Unary);
+    SwitchPackOp(f, opType, I32X4::Unary, F32X4::Unary, B32X4::Unary);
     f.writeU8(uint8_t(op));
     if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
         return false;
     *type = opType;
     return true;
 }
 
 template<class OpKind>
@@ -4495,40 +4566,46 @@ CheckSimdBinaryGuts(FunctionValidator& f
     *type = opType;
     return true;
 }
 
 static bool
 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                 MSimdBinaryArith::Operation op, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::Binary, F32X4::Binary);
+    SwitchPackOp(f, opType, I32X4::Binary, F32X4::Binary, B32X4::Binary);
     return CheckSimdBinaryGuts(f, call, opType, op, type);
 }
 
 static bool
 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                 MSimdBinaryBitwise::Operation op, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::BinaryBitwise, F32X4::Bad);
+    SwitchPackOp(f, opType, I32X4::BinaryBitwise, F32X4::Bad, B32X4::BinaryBitwise);
     return CheckSimdBinaryGuts(f, call, opType, op, type);
 }
 
 static bool
 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                 MSimdBinaryComp::Operation op, Type* type)
 {
     switch (opType) {
-      case AsmJSSimdType_int32x4:   f.writeOp(I32X4::BinaryCompI32X4); break;
-      case AsmJSSimdType_float32x4: f.writeOp(I32X4::BinaryCompF32X4); break;
+      case AsmJSSimdType_int32x4:
+        f.writeOp(B32X4::BinaryCompI32X4);
+        break;
+      case AsmJSSimdType_float32x4:
+        f.writeOp(B32X4::BinaryCompF32X4);
+        break;
+      case AsmJSSimdType_bool32x4:
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Can't compare boolean vectors");
     }
     f.writeU8(uint8_t(op));
     if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
         return false;
-    *type = Type::Int32x4;
+    *type = Type::Bool32x4;
     return true;
 }
 
 static bool
 CheckSimdBinary(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                 MSimdShift::Operation op, Type* type)
 {
     f.writeOp(I32X4::BinaryShift);
@@ -4546,24 +4623,28 @@ CheckSimdExtractLane(FunctionValidator& 
       case AsmJSSimdType_int32x4:
         f.writeOp(I32::I32X4ExtractLane);
         *type = Type::Signed;
         break;
       case AsmJSSimdType_float32x4:
         f.writeOp(F32::F32X4ExtractLane);
         *type = Type::Float;
         break;
+      case AsmJSSimdType_bool32x4:
+        f.writeOp(I32::B32X4ExtractLane);
+        *type = Type::Int;
+        break;
     }
     return CheckSimdCallArgs(f, call, 2, CheckSimdExtractLaneArgs(opType));
 }
 
 static bool
 CheckSimdReplaceLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::ReplaceLane, F32X4::ReplaceLane);
+    SwitchPackOp(f, opType, I32X4::ReplaceLane, F32X4::ReplaceLane, B32X4::ReplaceLane);
     if (!CheckSimdCallArgsPatchable(f, call, 3, CheckSimdReplaceLaneArgs(opType)))
         return false;
     *type = opType;
     return true;
 }
 
 typedef bool IsBitCast;
 
@@ -4571,17 +4652,18 @@ namespace {
 // Include CheckSimdCast in unnamed namespace to avoid MSVC name lookup bug (due to the use of Type).
 
 static bool
 CheckSimdCast(FunctionValidator& f, ParseNode* call, AsmJSSimdType fromType, AsmJSSimdType toType,
               bool bitcast, Type* type)
 {
     SwitchPackOp(f, toType,
                  bitcast ? I32X4::FromF32X4Bits : I32X4::FromF32X4,
-                 bitcast ? F32X4::FromI32X4Bits : F32X4::FromI32X4);
+                 bitcast ? F32X4::FromI32X4Bits : F32X4::FromI32X4,
+                 B32X4::Bad);
     if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType)))
         return false;
     *type = toType;
     return true;
 }
 
 } // namespace
 
@@ -4601,17 +4683,17 @@ CheckSimdShuffleSelectors(FunctionValida
 
 static bool
 CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
     if (numArgs != 5)
         return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs);
 
-    SwitchPackOp(f, opType, I32X4::Swizzle, F32X4::Swizzle);
+    SwitchPackOp(f, opType, I32X4::Swizzle, F32X4::Swizzle, B32X4::Bad);
 
     Type retType = opType;
     ParseNode* vec = CallArgList(call);
     Type vecType;
     if (!CheckExpr(f, vec, &vecType))
         return false;
     if (!(vecType <= retType))
         return f.failf(vec, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
@@ -4629,17 +4711,17 @@ CheckSimdSwizzle(FunctionValidator& f, P
 
 static bool
 CheckSimdShuffle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
     if (numArgs != 6)
         return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs);
 
-    SwitchPackOp(f, opType, I32X4::Shuffle, F32X4::Shuffle);
+    SwitchPackOp(f, opType, I32X4::Shuffle, F32X4::Shuffle, B32X4::Bad);
 
     Type retType = opType;
     ParseNode* arg = CallArgList(call);
     for (unsigned i = 0; i < 2; i++, arg = NextNode(arg)) {
         Type type;
         if (!CheckExpr(f, arg, &type))
             return false;
         if (!(type <= retType))
@@ -4673,16 +4755,17 @@ CheckSimdLoadStoreArgs(FunctionValidator
         return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
     }
 
     *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
 
     switch (opType) {
       case AsmJSSimdType_int32x4:   *viewType = Scalar::Int32x4;   break;
       case AsmJSSimdType_float32x4: *viewType = Scalar::Float32x4; break;
+      case AsmJSSimdType_bool32x4:  MOZ_CRASH("Cannot load/store boolean SIMD type");
     }
 
     ParseNode* indexExpr = NextNode(view);
     uint32_t indexLit;
     if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
         if (indexLit > INT32_MAX)
             return f.fail(indexExpr, "constant index out of range");
 
@@ -4713,17 +4796,17 @@ CheckSimdLoadStoreArgs(FunctionValidator
 static bool
 CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
               unsigned numElems, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
     if (numArgs != 2)
         return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs);
 
-    SwitchPackOp(f, opType, I32X4::Load, F32X4::Load);
+    SwitchPackOp(f, opType, I32X4::Load, F32X4::Load, B32X4::Bad);
     size_t viewTypeAt = f.tempU8();
     size_t needsBoundsCheckAt = f.tempU8();
     f.writeU8(numElems);
 
     Scalar::Type viewType;
     NeedsBoundsCheck needsBoundsCheck;
     if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &needsBoundsCheck))
         return false;
@@ -4738,17 +4821,17 @@ CheckSimdLoad(FunctionValidator& f, Pars
 static bool
 CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
                unsigned numElems, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
     if (numArgs != 3)
         return f.failf(call, "expected 3 arguments to SIMD store, got %u", numArgs);
 
-    SwitchPackOp(f, opType, I32X4::Store, F32X4::Store);
+    SwitchPackOp(f, opType, I32X4::Store, F32X4::Store, B32X4::Bad);
     size_t viewTypeAt = f.tempU8();
     size_t needsBoundsCheckAt = f.tempU8();
     f.writeU8(numElems);
 
     Scalar::Type viewType;
     NeedsBoundsCheck needsBoundsCheck;
     if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &needsBoundsCheck))
         return false;
@@ -4766,37 +4849,71 @@ CheckSimdStore(FunctionValidator& f, Par
 
     *type = vecType;
     return true;
 }
 
 static bool
 CheckSimdSelect(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::Select, F32X4::Select);
+    SwitchPackOp(f, opType, I32X4::Select, F32X4::Select, B32X4::Bad);
     if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType)))
         return false;
     *type = opType;
     return true;
 }
 
 static bool
+CheckSimdAllTrue(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
+{
+    switch (opType) {
+      case AsmJSSimdType_bool32x4:
+        f.writeOp(I32::B32X4AllTrue);
+        break;
+      case AsmJSSimdType_int32x4:
+      case AsmJSSimdType_float32x4:
+        MOZ_CRASH("allTrue is only defined on bool SIMD types");
+    }
+    if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
+        return false;
+    *type = Type::Int;
+    return true;
+}
+
+static bool
+CheckSimdAnyTrue(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
+{
+    switch (opType) {
+      case AsmJSSimdType_bool32x4:
+        f.writeOp(I32::B32X4AnyTrue);
+        break;
+      case AsmJSSimdType_int32x4:
+      case AsmJSSimdType_float32x4:
+        MOZ_CRASH("anyTrue is only defined on bool SIMD types");
+    }
+    if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
+        return false;
+    *type = Type::Int;
+    return true;
+}
+
+static bool
 CheckSimdCheck(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
     ValType coerceTo;
     ParseNode* argNode;
     if (!IsCoercionCall(f.m(), call, &coerceTo, &argNode))
         return f.failf(call, "expected 1 argument in call to check");
     return CheckCoercionArg(f, argNode, coerceTo, type);
 }
 
 static bool
 CheckSimdSplat(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Type* type)
 {
-    SwitchPackOp(f, opType, I32X4::Splat, F32X4::Splat);
+    SwitchPackOp(f, opType, I32X4::Splat, F32X4::Splat, B32X4::Splat);
     if (!CheckSimdCallArgsPatchable(f, call, 1, CheckSimdScalarArgs(opType)))
         return false;
     *type = opType;
     return true;
 }
 
 static bool
 CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
@@ -4895,30 +5012,31 @@ CheckSimdOperationCall(FunctionValidator
 
       case AsmJSSimdOperation_select:
         return CheckSimdSelect(f, call, opType, type);
 
       case AsmJSSimdOperation_splat:
         return CheckSimdSplat(f, call, opType, type);
 
       case AsmJSSimdOperation_allTrue:
+        return CheckSimdAllTrue(f, call, opType, type);
       case AsmJSSimdOperation_anyTrue:
-        MOZ_CRASH("unreachable and nyi"); // TODO bug 1160971
+        return CheckSimdAnyTrue(f, call, opType, type);
     }
     MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
 }
 
 static bool
 CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                   Type* type)
 {
     MOZ_ASSERT(call->isKind(PNK_CALL));
 
     AsmJSSimdType simdType = global->simdCtorType();
-    SwitchPackOp(f, simdType, I32X4::Ctor, F32X4::Ctor);
+    SwitchPackOp(f, simdType, I32X4::Ctor, F32X4::Ctor, B32X4::Ctor);
 
     unsigned length = SimdTypeToLength(simdType);
     if (!CheckSimdCallArgsPatchable(f, call, length, CheckSimdScalarArgs(simdType)))
         return false;
 
     *type = simdType;
     return true;
 }
@@ -4959,16 +5077,18 @@ CoerceResult(FunctionValidator& f, Parse
         else if (actual.isFloatish())
             f.patchOp(patchAt, Stmt::F32Expr);
         else if (actual.isMaybeDouble())
             f.patchOp(patchAt, Stmt::F64Expr);
         else if (actual.isInt32x4())
             f.patchOp(patchAt, Stmt::I32X4Expr);
         else if (actual.isFloat32x4())
             f.patchOp(patchAt, Stmt::F32X4Expr);
+        else if (actual.isBool32x4())
+            f.patchOp(patchAt, Stmt::B32X4Expr);
         else if (actual.isVoid())
             f.patchOp(patchAt, Stmt::Id);
         else
             MOZ_CRASH("unhandled return type");
         break;
       case ExprType::I32:
         if (!actual.isIntish())
             return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
@@ -4997,16 +5117,21 @@ CoerceResult(FunctionValidator& f, Parse
             return f.failf(expr, "%s is not a subtype of int32x4", actual.toChars());
         f.patchOp(patchAt, I32X4::Id);
         break;
       case ExprType::F32x4:
         if (!actual.isFloat32x4())
             return f.failf(expr, "%s is not a subtype of float32x4", actual.toChars());
         f.patchOp(patchAt, F32X4::Id);
         break;
+      case ExprType::B32x4:
+        if (!actual.isBool32x4())
+            return f.failf(expr, "%s is not a subtype of bool32x4", actual.toChars());
+        f.patchOp(patchAt, B32X4::Id);
+        break;
     }
 
     *type = Type::ret(expected);
     return true;
 }
 
 static bool
 CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
@@ -5029,17 +5154,16 @@ CheckCoercedSimdCall(FunctionValidator& 
     if (global->isSimdCtor()) {
         if (!CheckSimdCtorCall(f, call, global, &actual))
             return false;
         MOZ_ASSERT(actual.isSimd());
     } else {
         MOZ_ASSERT(global->isSimdOperation());
         if (!CheckSimdOperationCall(f, call, global, &actual))
             return false;
-        MOZ_ASSERT_IF(global->simdOperation() != AsmJSSimdOperation_extractLane, actual.isSimd());
     }
 
     return CoerceResult(f, call, ret, actual, opcodeAt, type);
 }
 
 static bool
 CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode,
                                AsmJSAtomicsBuiltinFunction func, ExprType ret, Type* type)
@@ -5244,16 +5368,18 @@ CheckComma(FunctionValidator& f, ParseNo
     else if (type->isFloatish())
         f.patchOp(commaAt, F32::Comma);
     else if (type->isMaybeDouble())
         f.patchOp(commaAt, F64::Comma);
     else if (type->isInt32x4())
         f.patchOp(commaAt, I32X4::Comma);
     else if (type->isFloat32x4())
         f.patchOp(commaAt, F32X4::Comma);
+    else if (type->isBool32x4())
+        f.patchOp(commaAt, B32X4::Comma);
     else
         MOZ_CRASH("unexpected or unimplemented expression statement");
 
     return true;
 }
 
 static bool
 CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
@@ -5291,16 +5417,19 @@ CheckConditional(FunctionValidator& f, P
         f.patchOp(opcodeAt, F32::Conditional);
         *type = Type::Float;
     } else if (elseType.isInt32x4() && thenType.isInt32x4()) {
         f.patchOp(opcodeAt, I32X4::Conditional);
         *type = Type::Int32x4;
     } else if (elseType.isFloat32x4() && thenType.isFloat32x4()) {
         f.patchOp(opcodeAt, F32X4::Conditional);
         *type = Type::Float32x4;
+    } else if (elseType.isBool32x4() && thenType.isBool32x4()) {
+        f.patchOp(opcodeAt, B32X4::Conditional);
+        *type = Type::Bool32x4;
     } else {
         return f.failf(ternary, "then/else branches of conditional must both produce int, float, "
                        "double or SIMD types, current types are %s and %s",
                        thenType.toChars(), elseType.toChars());
     }
 
     return true;
 }
@@ -5319,16 +5448,17 @@ IsValidIntMultiplyConstant(ModuleValidat
             return true;
         return false;
       case NumLit::BigUnsigned:
       case NumLit::Double:
       case NumLit::Float:
       case NumLit::OutOfRangeInt:
       case NumLit::Int32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool32x4:
         return false;
     }
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
 CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type)
@@ -5687,16 +5817,18 @@ CheckAsExprStatement(FunctionValidator& 
     else if (type.isFloatish())
         f.patchOp(opcodeAt, Stmt::F32Expr);
     else if (type.isMaybeDouble())
         f.patchOp(opcodeAt, Stmt::F64Expr);
     else if (type.isInt32x4())
         f.patchOp(opcodeAt, Stmt::I32X4Expr);
     else if (type.isFloat32x4())
         f.patchOp(opcodeAt, Stmt::F32X4Expr);
+    else if (type.isBool32x4())
+        f.patchOp(opcodeAt, Stmt::B32X4Expr);
     else
         MOZ_CRASH("unexpected or unimplemented expression statement");
 
     return true;
 }
 
 static bool
 CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt)
@@ -5892,16 +6024,17 @@ CheckCaseExpr(FunctionValidator& f, Pars
         break;
       case NumLit::OutOfRangeInt:
       case NumLit::BigUnsigned:
         return f.fail(caseExpr, "switch case expression out of integer range");
       case NumLit::Double:
       case NumLit::Float:
       case NumLit::Int32x4:
       case NumLit::Float32x4:
+      case NumLit::Bool32x4:
         return f.fail(caseExpr, "switch case expression must be an integer literal");
     }
 
     return true;
 }
 
 static bool
 CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt)
@@ -6070,16 +6203,18 @@ CheckReturn(FunctionValidator& f, ParseN
     else if (type.isFloat())
         ret = ExprType::F32;
     else if (type.isDouble())
         ret = ExprType::F64;
     else if (type.isInt32x4())
         ret = ExprType::I32x4;
     else if (type.isFloat32x4())
         ret = ExprType::F32x4;
+    else if (type.isBool32x4())
+        ret = ExprType::B32x4;
     else if (type.isVoid())
         ret = ExprType::Void;
     else
         return f.failf(expr, "%s is not a valid return type", type.toChars());
 
     return CheckReturnType(f, expr, ret);
 }
 
--- a/js/src/asmjs/Wasm.h
+++ b/js/src/asmjs/Wasm.h
@@ -38,35 +38,43 @@ using mozilla::Move;
 // causes miscompilations in GCC (fixed in 4.8.5 and 4.9.3).
 enum class ValType
 {
     I32,
     I64,
     F32,
     F64,
     I32x4,
-    F32x4
+    F32x4,
+    B32x4
 };
 
 static inline bool
 IsSimdType(ValType vt)
 {
-    return vt == ValType::I32x4 || vt == ValType::F32x4;
+    return vt == ValType::I32x4 || vt == ValType::F32x4 || vt == ValType::B32x4;
+}
+
+static inline bool
+IsSimdBoolType(ValType vt)
+{
+    return vt == ValType::B32x4;
 }
 
 static inline jit::MIRType
 ToMIRType(ValType vt)
 {
     switch (vt) {
       case ValType::I32: return jit::MIRType_Int32;
       case ValType::I64: MOZ_CRASH("NYI");
       case ValType::F32: return jit::MIRType_Float32;
       case ValType::F64: return jit::MIRType_Double;
       case ValType::I32x4: return jit::MIRType_Int32x4;
       case ValType::F32x4: return jit::MIRType_Float32x4;
+      case ValType::B32x4: return jit::MIRType_Bool32x4;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
 }
 
 // The Val class represents a single WebAssembly value of a given value type,
 // mostly for the purpose of numeric literals and initializers. A Val does not
 // directly map to a JS value since there is not (currently) a precise
 // representation of i64 values. A Val may contain non-canonical NaNs since,
@@ -92,27 +100,34 @@ class Val
 
   public:
     Val() = default;
 
     explicit Val(uint32_t i32) : type_(ValType::I32) { u.i32_ = i32; }
     explicit Val(uint64_t i64) : type_(ValType::I64) { u.i64_ = i64; }
     explicit Val(float f32) : type_(ValType::F32) { u.f32_ = f32; }
     explicit Val(double f64) : type_(ValType::F64) { u.f64_ = f64; }
-    explicit Val(const I32x4& i32x4) : type_(ValType::I32x4) { memcpy(u.i32x4_, i32x4, sizeof(u.i32x4_)); }
+
+    explicit Val(const I32x4& i32x4, ValType type = ValType::I32x4) : type_(type) {
+        MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
+        memcpy(u.i32x4_, i32x4, sizeof(u.i32x4_));
+    }
     explicit Val(const F32x4& f32x4) : type_(ValType::F32x4) { memcpy(u.f32x4_, f32x4, sizeof(u.f32x4_)); }
 
     ValType type() const { return type_; }
     bool isSimd() const { return IsSimdType(type()); }
 
     uint32_t i32() const { MOZ_ASSERT(type_ == ValType::I32); return u.i32_; }
     uint64_t i64() const { MOZ_ASSERT(type_ == ValType::I64); return u.i64_; }
     float f32() const { MOZ_ASSERT(type_ == ValType::F32); return u.f32_; }
     double f64() const { MOZ_ASSERT(type_ == ValType::F64); return u.f64_; }
-    const I32x4& i32x4() const { MOZ_ASSERT(type_ == ValType::I32x4); return u.i32x4_; }
+    const I32x4& i32x4() const {
+        MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
+        return u.i32x4_;
+    }
     const F32x4& f32x4() const { MOZ_ASSERT(type_ == ValType::F32x4); return u.f32x4_; }
 };
 
 // The ExprType enum represents the type of a WebAssembly expression or return
 // value and may either be a value type or void. A future WebAssembly extension
 // may generalize expression types to instead be a list of value types (with
 // void represented by the empty list). For now it's easier to have a flat enum
 // and be explicit about conversions to/from value types.
@@ -120,16 +135,17 @@ class Val
 enum class ExprType : uint8_t
 {
     I32 = uint8_t(ValType::I32),
     I64 = uint8_t(ValType::I64),
     F32 = uint8_t(ValType::F32),
     F64 = uint8_t(ValType::F64),
     I32x4 = uint8_t(ValType::I32x4),
     F32x4 = uint8_t(ValType::F32x4),
+    B32x4 = uint8_t(ValType::B32x4),
     Void
 };
 
 static inline bool
 IsVoid(ExprType et)
 {
     return et == ExprType::Void;
 }
--- a/js/src/asmjs/WasmIR.h
+++ b/js/src/asmjs/WasmIR.h
@@ -59,16 +59,17 @@ enum class Stmt : uint8_t
 
     // asm.js specific
     // Expression statements (to be removed in the future)
     I32Expr,
     F32Expr,
     F64Expr,
     I32X4Expr,
     F32X4Expr,
+    B32X4Expr,
 
     Id,
     Noop,
     InterruptCheckHead,
     InterruptCheckLoop,
 
     DebugCheckPoint,
 
@@ -168,16 +169,19 @@ enum class I32 : uint8_t
     AtomicsCompareExchange,
     AtomicsExchange,
     AtomicsLoad,
     AtomicsStore,
     AtomicsBinOp,
 
     // SIMD opcodes
     I32X4ExtractLane,
+    B32X4ExtractLane,
+    B32X4AllTrue,
+    B32X4AnyTrue,
 
     // Specific to AsmJS
     Id,
 
     Bad
 };
 
 enum class F32 : uint8_t
@@ -307,18 +311,16 @@ enum class I32X4 : uint8_t
     Literal,
 
     // Specific opcodes
     Ctor,
 
     Unary,
 
     Binary,
-    BinaryCompI32X4,
-    BinaryCompF32X4,
     BinaryBitwise,
     BinaryShift,
 
     ReplaceLane,
 
     FromF32X4,
     FromF32X4Bits,
 
@@ -372,16 +374,53 @@ enum class F32X4 : uint8_t
     Load,
     Store,
 
     // asm.js specific
     Id,
     Bad
 };
 
+enum class B32X4 : uint8_t
+{
+    // Common opcodes
+    GetLocal,
+    SetLocal,
+
+    GetGlobal,
+    SetGlobal,
+
+    CallInternal,
+    CallIndirect,
+    CallImport,
+
+    Conditional,
+    Comma,
+
+    Literal,
+
+    // Specific opcodes
+    Ctor,
+
+    Unary,
+
+    Binary,
+    BinaryCompI32X4,
+    BinaryCompF32X4,
+    BinaryBitwise,
+
+    ReplaceLane,
+
+    Splat,
+
+    // asm.js specific
+    Id,
+    Bad
+};
+
 enum NeedsBoundsCheck : uint8_t
 {
     NO_BOUNDS_CHECK,
     NEEDS_BOUNDS_CHECK
 };
 
 // The FuncIR class contains the intermediate representation of a parsed/decoded
 // and validated asm.js/WebAssembly function. The FuncIR lives only until it
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -124,16 +124,20 @@ class FunctionCompiler
                 ins = MConstant::NewAsmJS(alloc(), DoubleValue(v.f64()), MIRType_Double);
                 break;
               case ValType::I32x4:
                 ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.i32x4()), MIRType_Int32x4);
                 break;
               case ValType::F32x4:
                 ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.f32x4()), MIRType_Float32x4);
                 break;
+              case ValType::B32x4:
+                // Bool32x4 uses the same data layout as Int32x4.
+                ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.i32x4()), MIRType_Bool32x4);
+                break;
             }
 
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(firstVarSlot + i), ins);
             if (!mirGen_.ensureBallast())
                 return false;
             if (!localTypes_.append(v.type()))
                 return false;
@@ -310,24 +314,43 @@ class FunctionCompiler
     }
 
     MDefinition* selectSimd(MDefinition* mask, MDefinition* lhs, MDefinition* rhs, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(mask->type()));
-        MOZ_ASSERT(mask->type() == MIRType_Int32x4);
         MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type());
         MOZ_ASSERT(lhs->type() == type);
         MSimdSelect* ins = MSimdSelect::NewAsmJS(alloc(), mask, lhs, rhs, type);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition* simdAllTrue(MDefinition* boolVector)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        MSimdAllTrue* ins = MSimdAllTrue::NewAsmJS(alloc(), boolVector);
+        curBlock_->add(ins);
+        return ins;
+    }
+
+    MDefinition* simdAnyTrue(MDefinition* boolVector)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        MSimdAnyTrue* ins = MSimdAnyTrue::NewAsmJS(alloc(), boolVector);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     template<class T>
     MDefinition* convertSimd(MDefinition* vec, MIRType from, MIRType to)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(from) && IsSimdType(to) && from != to);
         T* ins = T::NewAsmJS(alloc(), vec, from, to);
@@ -1285,16 +1308,22 @@ EmitLiteral(FunctionCompiler& f, ValType
         *def = f.constant(lit, MIRType_Int32x4);
         return true;
       }
       case ValType::F32x4: {
         SimdConstant lit(f.readF32X4());
         *def = f.constant(lit, MIRType_Float32x4);
         return true;
       }
+      case ValType::B32x4: {
+        // Boolean vectors are stored as an Int vector with -1 / 0 lanes.
+        SimdConstant lit(f.readI32X4());
+        *def = f.constant(lit, MIRType_Bool32x4);
+        return true;
+      }
     }
     MOZ_CRASH("unexpected literal type");
 }
 
 static bool
 EmitGetLoc(FunctionCompiler& f, const DebugOnly<MIRType>& type, MDefinition** def)
 {
     uint32_t slot = f.readU32();
@@ -1312,17 +1341,19 @@ EmitGetGlo(FunctionCompiler& f, MIRType 
     return true;
 }
 
 static bool EmitI32Expr(FunctionCompiler& f, MDefinition** def);
 static bool EmitF32Expr(FunctionCompiler& f, MDefinition** def);
 static bool EmitF64Expr(FunctionCompiler& f, MDefinition** def);
 static bool EmitI32X4Expr(FunctionCompiler& f, MDefinition** def);
 static bool EmitF32X4Expr(FunctionCompiler& f, MDefinition** def);
+static bool EmitB32X4Expr(FunctionCompiler& f, MDefinition** def);
 static bool EmitExpr(FunctionCompiler& f, ValType type, MDefinition** def);
+static bool EmitSimdBooleanLaneExpr(FunctionCompiler& f, MDefinition** def);
 
 static bool
 EmitLoadArray(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
 {
     NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
     MDefinition* ptr;
     if (!EmitI32Expr(f, &ptr))
         return false;
@@ -1521,16 +1552,17 @@ EmitCallArgs(FunctionCompiler& f, const 
         MDefinition *arg = nullptr;
         switch (sig.arg(i)) {
           case ValType::I32:    if (!EmitI32Expr(f, &arg))   return false; break;
           case ValType::I64:    MOZ_CRASH("int64");
           case ValType::F32:    if (!EmitF32Expr(f, &arg))   return false; break;
           case ValType::F64:    if (!EmitF64Expr(f, &arg))   return false; break;
           case ValType::I32x4:  if (!EmitI32X4Expr(f, &arg)) return false; break;
           case ValType::F32x4:  if (!EmitF32X4Expr(f, &arg)) return false; break;
+          case ValType::B32x4:  if (!EmitB32X4Expr(f, &arg)) return false; break;
         }
         if (!f.passArg(arg, sig.arg(i), call))
             return false;
     }
     f.finishCallArgs(call);
     return true;
 }
 
@@ -1729,16 +1761,17 @@ EmitSimdBinaryShift(FunctionCompiler& f,
 }
 
 static ValType
 SimdToLaneType(ValType type)
 {
     switch (type) {
       case ValType::I32x4:  return ValType::I32;
       case ValType::F32x4:  return ValType::F32;
+      case ValType::B32x4:  return ValType::I32; // Boolean lanes are Int32 in asm.
       case ValType::I32:
       case ValType::I64:
       case ValType::F32:
       case ValType::F64:;
     }
     MOZ_CRASH("bad simd type");
 }
 
@@ -1784,18 +1817,22 @@ EmitSimdReplaceLane(FunctionCompiler& f,
         int32_t laneLit = laneDef->toConstant()->value().toInt32();
         MOZ_ASSERT(laneLit < 4);
         lane = SimdLane(laneLit);
     } else {
         lane = SimdLane(-1);
     }
 
     MDefinition* scalar;
-    if (!EmitExpr(f, SimdToLaneType(simdType), &scalar))
-        return false;
+    if (IsSimdBoolType(simdType)) {
+        if (!EmitSimdBooleanLaneExpr(f, &scalar))
+            return false;
+    } else if (!EmitExpr(f, SimdToLaneType(simdType), &scalar)) {
+            return false;
+    }
     *def = f.insertElementSimd(vector, scalar, lane, ToMIRType(simdType));
     return true;
 }
 
 template<class T>
 inline bool
 EmitSimdCast(FunctionCompiler& f, ValType fromType, ValType toType, MDefinition** def)
 {
@@ -1873,34 +1910,70 @@ EmitSimdStore(FunctionCompiler& f, ValTy
     f.storeSimdHeap(viewType, index, vec, needsBoundsCheck, numElems);
     *def = vec;
     return true;
 }
 
 static bool
 EmitSimdSelect(FunctionCompiler& f, ValType type, MDefinition** def)
 {
-    MDefinition* defs[3];
-    if (!EmitI32X4Expr(f, &defs[0]) || !EmitExpr(f, type, &defs[1]) || !EmitExpr(f, type, &defs[2]))
+    MDefinition* mask;
+    MDefinition* defs[2];
+
+    // The mask is a boolean vector for elementwise select.
+    if (!EmitB32X4Expr(f, &mask))
+        return false;
+
+    if (!EmitExpr(f, type, &defs[0]) || !EmitExpr(f, type, &defs[1]))
         return false;
-    *def = f.selectSimd(defs[0], defs[1], defs[2], ToMIRType(type));
+    *def = f.selectSimd(mask, defs[0], defs[1], ToMIRType(type));
+    return true;
+}
+
+static bool
+EmitSimdAllTrue(FunctionCompiler& f, ValType type, MDefinition** def)
+{
+    MDefinition* in;
+    if (!EmitExpr(f, type, &in))
+        return false;
+    *def = f.simdAllTrue(in);
+    return true;
+}
+
+static bool
+EmitSimdAnyTrue(FunctionCompiler& f, ValType type, MDefinition** def)
+{
+    MDefinition* in;
+    if (!EmitExpr(f, type, &in))
+        return false;
+    *def = f.simdAnyTrue(in);
     return true;
 }
 
 static bool
 EmitSimdSplat(FunctionCompiler& f, ValType type, MDefinition** def)
 {
     MDefinition* in;
     if (!EmitExpr(f, SimdToLaneType(type), &in))
         return false;
     *def = f.splatSimd(in, ToMIRType(type));
     return true;
 }
 
 static bool
+EmitSimdBooleanSplat(FunctionCompiler& f, MDefinition** def)
+{
+    MDefinition* in;
+    if (!EmitSimdBooleanLaneExpr(f, &in))
+        return false;
+    *def = f.splatSimd(in, MIRType_Bool32x4);
+    return true;
+}
+
+static bool
 EmitSimdCtor(FunctionCompiler& f, ValType type, MDefinition** def)
 {
     switch (type) {
       case ValType::I32x4: {
         MDefinition* args[4];
         for (unsigned i = 0; i < 4; i++) {
             if (!EmitI32Expr(f, &args[i]))
                 return false;
@@ -1912,16 +1985,25 @@ EmitSimdCtor(FunctionCompiler& f, ValTyp
         MDefinition* args[4];
         for (unsigned i = 0; i < 4; i++) {
             if (!EmitF32Expr(f, &args[i]))
                 return false;
         }
         *def = f.constructSimd<MSimdValueX4>(args[0], args[1], args[2], args[3], MIRType_Float32x4);
         return true;
       }
+      case ValType::B32x4: {
+        MDefinition* args[4];
+        for (unsigned i = 0; i < 4; i++) {
+            if (!EmitSimdBooleanLaneExpr(f, &args[i]))
+                return false;
+        }
+        *def = f.constructSimd<MSimdValueX4>(args[0], args[1], args[2], args[3], MIRType_Bool32x4);
+        return true;
+      }
       case ValType::I32:
       case ValType::I64:
       case ValType::F32:
       case ValType::F64:;
     }
     MOZ_CRASH("unexpected SIMD type");
 }
 
@@ -2172,20 +2254,34 @@ EmitExpr(FunctionCompiler& f, ValType ty
 {
     switch (type) {
       case ValType::I32:    return EmitI32Expr(f, def);
       case ValType::I64:    MOZ_CRASH("int64");
       case ValType::F32:    return EmitF32Expr(f, def);
       case ValType::F64:    return EmitF64Expr(f, def);
       case ValType::I32x4:  return EmitI32X4Expr(f, def);
       case ValType::F32x4:  return EmitF32X4Expr(f, def);
+      case ValType::B32x4:  return EmitB32X4Expr(f, def);
     }
     MOZ_CRASH("unexpected asm type");
 }
 
+// Emit an I32 expression and then convert it to a boolean SIMD lane value, i.e. -1 or 0.
+static bool
+EmitSimdBooleanLaneExpr(FunctionCompiler& f, MDefinition** def)
+{
+    MDefinition* i32;
+    if (!EmitI32Expr(f, &i32))
+        return false;
+    // Now compute !i32 - 1 to force the value range into {0, -1}.
+    MDefinition* noti32 = f.unary<MNot>(i32);
+    *def = f.binary<MSub>(noti32, f.constant(Int32Value(1), MIRType_Int32), MIRType_Int32);
+    return true;
+}
+
 static bool
 EmitInterruptCheck(FunctionCompiler& f)
 {
     unsigned lineno = f.readU32();
     unsigned column = f.readU32();
     f.addInterruptCheck(lineno, column);
     return true;
 }
@@ -2475,16 +2571,17 @@ EmitStatement(FunctionCompiler& f, Stmt 
       case Stmt::Break:              return EmitBreak(f, HasLabel(false));
       case Stmt::BreakLabel:         return EmitBreak(f, HasLabel(true));
       case Stmt::Ret:                return EmitRet(f);
       case Stmt::I32Expr:            return EmitI32Expr(f, &_);
       case Stmt::F32Expr:            return EmitF32Expr(f, &_);
       case Stmt::F64Expr:            return EmitF64Expr(f, &_);
       case Stmt::I32X4Expr:          return EmitI32X4Expr(f, &_);
       case Stmt::F32X4Expr:          return EmitF32X4Expr(f, &_);
+      case Stmt::B32X4Expr:          return EmitB32X4Expr(f, &_);
       case Stmt::CallInternal:       return EmitInternalCall(f, ExprType::Void, &_);
       case Stmt::CallIndirect:       return EmitFuncPtrCall(f, ExprType::Void, &_);
       case Stmt::CallImport:         return EmitFFICall(f, ExprType::Void, &_);
       case Stmt::AtomicsFence:       f.memoryBarrier(MembarFull); return true;
       case Stmt::Noop:               return true;
       case Stmt::Id:                 return EmitStatement(f);
       case Stmt::InterruptCheckHead: return EmitInterruptCheck(f);
       case Stmt::InterruptCheckLoop: return EmitInterruptCheckLoop(f);
@@ -2618,16 +2715,22 @@ EmitI32Expr(FunctionCompiler& f, MDefini
       case I32::AtomicsLoad:
         return EmitAtomicsLoad(f, def);
       case I32::AtomicsStore:
         return EmitAtomicsStore(f, def);
       case I32::AtomicsBinOp:
         return EmitAtomicsBinOp(f, def);
       case I32::I32X4ExtractLane:
         return EmitExtractLane(f, ValType::I32x4, def);
+      case I32::B32X4ExtractLane:
+        return EmitExtractLane(f, ValType::B32x4, def);
+      case I32::B32X4AllTrue:
+        return EmitSimdAllTrue(f, ValType::B32x4, def);
+      case I32::B32X4AnyTrue:
+        return EmitSimdAnyTrue(f, ValType::B32x4, def);
       case I32::Bad:
         break;
     }
     MOZ_CRASH("unexpected i32 expression");
 }
 
 static bool
 EmitF32Expr(FunctionCompiler& f, MDefinition** def)
@@ -2805,20 +2908,16 @@ EmitI32X4Expr(FunctionCompiler& f, MDefi
       case I32X4::Ctor:
         return EmitSimdCtor(f, ValType::I32x4, def);
       case I32X4::Unary:
         return EmitSimdUnary(f, ValType::I32x4, def);
       case I32X4::Binary:
         return EmitSimdBinaryArith(f, ValType::I32x4, def);
       case I32X4::BinaryBitwise:
         return EmitSimdBinaryBitwise(f, ValType::I32x4, def);
-      case I32X4::BinaryCompI32X4:
-        return EmitSimdBinaryComp(f, ValType::I32x4, def);
-      case I32X4::BinaryCompF32X4:
-        return EmitSimdBinaryComp(f, ValType::F32x4, def);
       case I32X4::BinaryShift:
         return EmitSimdBinaryShift(f, def);
       case I32X4::ReplaceLane:
         return EmitSimdReplaceLane(f, ValType::I32x4, def);
       case I32X4::FromF32X4:
         return EmitSimdCast<MSimdConvert>(f, ValType::F32x4, ValType::I32x4, def);
       case I32X4::FromF32X4Bits:
         return EmitSimdCast<MSimdReinterpretCast>(f, ValType::F32x4, ValType::I32x4, def);
@@ -2892,16 +2991,65 @@ EmitF32X4Expr(FunctionCompiler& f, MDefi
       case F32X4::Store:
         return EmitSimdStore(f, ValType::F32x4, def);
       case F32X4::Bad:
         break;
     }
     MOZ_CRASH("unexpected float32x4 expression");
 }
 
+static bool
+EmitB32X4Expr(FunctionCompiler& f, MDefinition** def)
+{
+    B32X4 op = B32X4(f.readU8());
+    switch (op) {
+      case B32X4::Id:
+        return EmitB32X4Expr(f, def);
+      case B32X4::GetLocal:
+        return EmitGetLoc(f, DebugOnly<MIRType>(MIRType_Bool32x4), def);
+      case B32X4::SetLocal:
+        return EmitSetLoc(f, ValType::B32x4, def);
+      case B32X4::GetGlobal:
+        return EmitGetGlo(f, MIRType_Bool32x4, def);
+      case B32X4::SetGlobal:
+        return EmitSetGlo(f, ValType::B32x4, def);
+      case B32X4::Comma:
+        return EmitComma(f, ValType::B32x4, def);
+      case B32X4::Conditional:
+        return EmitConditional(f, ValType::B32x4, def);
+      case B32X4::CallInternal:
+        return EmitInternalCall(f, ExprType::B32x4, def);
+      case B32X4::CallIndirect:
+        return EmitFuncPtrCall(f, ExprType::B32x4, def);
+      case B32X4::CallImport:
+        return EmitFFICall(f, ExprType::B32x4, def);
+      case B32X4::Literal:
+        return EmitLiteral(f, ValType::B32x4, def);
+      case B32X4::Ctor:
+        return EmitSimdCtor(f, ValType::B32x4, def);
+      case B32X4::Unary:
+        return EmitSimdUnary(f, ValType::B32x4, def);
+      case B32X4::Binary:
+        return EmitSimdBinaryArith(f, ValType::B32x4, def);
+      case B32X4::BinaryBitwise:
+        return EmitSimdBinaryBitwise(f, ValType::B32x4, def);
+      case B32X4::BinaryCompI32X4:
+        return EmitSimdBinaryComp(f, ValType::I32x4, def);
+      case B32X4::BinaryCompF32X4:
+        return EmitSimdBinaryComp(f, ValType::F32x4, def);
+      case B32X4::ReplaceLane:
+        return EmitSimdReplaceLane(f, ValType::B32x4, def);
+      case B32X4::Splat:
+        return EmitSimdBooleanSplat(f, def);
+      case B32X4::Bad:
+        break;
+    }
+    MOZ_CRASH("unexpected bool32x4 expression");
+}
+
 bool
 wasm::CompileFunction(CompileTask* task)
 {
     int64_t before = PRMJ_Now();
 
     CompileArgs args = task->args();
     const FuncIR& func = task->func();
     FunctionCompileResults& results = task->results();
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -185,16 +185,17 @@ GenerateEntry(MacroAssembler& masm, AsmJ
             MOZ_CRASH("AsmJS uses hardfp for function calls.");
             break;
 #endif
           case ABIArg::FPU: {
             static_assert(sizeof(AsmJSModule::EntryArg) >= jit::Simd128DataSize,
                           "EntryArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType_Int32x4:
+              case MIRType_Bool32x4:
                 masm.loadUnalignedInt32x4(src, iter->fpu());
                 break;
               case MIRType_Float32x4:
                 masm.loadUnalignedFloat32x4(src, iter->fpu());
                 break;
               case MIRType_Double:
                 masm.loadDouble(src, iter->fpu());
                 break;
@@ -217,16 +218,17 @@ GenerateEntry(MacroAssembler& masm, AsmJ
                 masm.loadDouble(src, ScratchDoubleReg);
                 masm.storeDouble(ScratchDoubleReg, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
                 break;
               case MIRType_Float32:
                 masm.loadFloat32(src, ScratchFloat32Reg);
                 masm.storeFloat32(ScratchFloat32Reg, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
                 break;
               case MIRType_Int32x4:
+              case MIRType_Bool32x4:
                 masm.loadUnalignedInt32x4(src, ScratchSimd128Reg);
                 masm.storeAlignedInt32x4(ScratchSimd128Reg,
                                          Address(masm.getStackPointer(), iter->offsetFromArgBase()));
                 break;
               case MIRType_Float32x4:
                 masm.loadUnalignedFloat32x4(src, ScratchSimd128Reg);
                 masm.storeAlignedFloat32x4(ScratchSimd128Reg,
                                            Address(masm.getStackPointer(), iter->offsetFromArgBase()));
@@ -264,16 +266,17 @@ GenerateEntry(MacroAssembler& masm, AsmJ
       case ExprType::F32:
         masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg);
         // Fall through as ReturnDoubleReg now contains a Double
       case ExprType::F64:
         masm.canonicalizeDouble(ReturnDoubleReg);
         masm.storeDouble(ReturnDoubleReg, Address(argv, 0));
         break;
       case ExprType::I32x4:
+      case ExprType::B32x4:
         // We don't have control on argv alignment, do an unaligned access.
         masm.storeUnalignedInt32x4(ReturnSimd128Reg, Address(argv, 0));
         break;
       case ExprType::F32x4:
         // We don't have control on argv alignment, do an unaligned access.
         masm.storeUnalignedFloat32x4(ReturnSimd128Reg, Address(argv, 0));
         break;
     }
@@ -544,16 +547,17 @@ GenerateInterpExit(MacroAssembler& masm,
         MOZ_CRASH("Float32 shouldn't be returned from a FFI");
       case ExprType::F64:
         masm.call(SymbolicAddress::InvokeFromAsmJS_ToNumber);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case ExprType::I32x4:
       case ExprType::F32x4:
+      case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
     // The heap pointer may have changed during the FFI, so reload it and test
     // for detachment.
     masm.loadAsmJSHeapRegisterFromGlobalData();
     CheckForHeapDetachment(masm, module, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
 
@@ -795,16 +799,17 @@ GenerateIonExit(MacroAssembler& masm, As
         MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:
         MOZ_CRASH("Float shouldn't be returned from a FFI");
       case ExprType::F64:
         masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
         break;
       case ExprType::I32x4:
       case ExprType::F32x4:
+      case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
     Label done;
     masm.bind(&done);
 
     // The heap pointer has to be reloaded anyway since Ion could have clobbered
     // it. Additionally, the FFI may have detached the heap buffer.
--- a/js/src/jit-test/tests/asm.js/simd-fbirds.js
+++ b/js/src/jit-test/tests/asm.js/simd-fbirds.js
@@ -53,26 +53,28 @@ var code = `
     const mk4 = 0x000ffff0;
 
     const getMaxPos = 1000.0;
     const getAccelDataSteps = imp.accelDataSteps | 0;
     var getActualBirds = imp.getActualBirds;
 
     var i4 = global.SIMD.Int32x4;
     var f4 = global.SIMD.Float32x4;
+    var b4 = global.SIMD.Bool32x4;
     var i4add = i4.add;
     var i4and = i4.and;
     var f4select = f4.select;
     var f4add = f4.add;
     var f4sub = f4.sub;
     var f4mul = f4.mul;
     var f4greaterThan = f4.greaterThan;
     var f4splat = f4.splat;
     var f4load = f4.load;
     var f4store = f4.store;
+    var b4any = b4.anyTrue;
 
     const zerox4 = f4(0.0,0.0,0.0,0.0);
 
     function declareHeapSize() {
         f32[0x0007ffff] = toF(0.0);
     }
 
     function update(timeDelta) {
@@ -90,17 +92,17 @@ var code = `
         var len = 0;
         var accelIndex = 0;
         var newPosx4 = f4(0.0,0.0,0.0,0.0);
         var newVelx4 = f4(0.0,0.0,0.0,0.0);
         var accel = toF(0.0);
         var accelx4 = f4(0.0,0.0,0.0,0.0);
         var a = 0;
         var posDeltax4 = f4(0.0,0.0,0.0,0.0);
-        var cmpx4 = i4(0,0,0,0);
+        var cmpx4 = b4(0,0,0,0);
         var newVelTruex4 = f4(0.0,0.0,0.0,0.0);
 
         steps = getAccelDataSteps | 0;
         subTimeDelta = toF(toF(timeDelta / toF(steps | 0)) / toF(1000.0));
         actualBirds = getActualBirds() | 0;
         maxPos = toF(+getMaxPos);
         maxPosx4 = f4splat(maxPos);
         subTimeDeltax4 = f4splat(subTimeDelta);
@@ -117,17 +119,17 @@ var code = `
                 accelx4 = f4splat(accel);
                 accelIndex = (accelIndex + 4) | 0;
                 posDeltax4 = f4mul(point5x4, f4mul(accelx4, subTimeDeltaSquaredx4));
                 posDeltax4 = f4add(posDeltax4, f4mul(newVelx4, subTimeDeltax4));
                 newPosx4 = f4add(newPosx4, posDeltax4);
                 newVelx4 = f4add(newVelx4, f4mul(accelx4, subTimeDeltax4));
                 cmpx4 = f4greaterThan(newPosx4, maxPosx4);
 
-                if (cmpx4.signMask) {
+                if (b4any(cmpx4)) {
                     // Work around unimplemented 'neg' operation, using 0 - x.
                     newVelTruex4 = f4sub(zerox4, newVelx4);
                     newVelx4 = f4select(cmpx4, newVelTruex4, newVelx4);
                 }
             }
             f4store(u8, i & mk4, newPosx4);
             f4store(u8, (i & mk4) + maxBirdsx4, newVelx4);
         }
--- a/js/src/jit-test/tests/asm.js/simd-mandelbrot.js
+++ b/js/src/jit-test/tests/asm.js/simd-mandelbrot.js
@@ -29,23 +29,26 @@ var moduleCode = `
   var b8 = new global.Uint8Array(buffer);
   var toF = global.Math.fround;
   var i4 = global.SIMD.Int32x4;
   var ci4 = i4.check;
   var f4 = global.SIMD.Float32x4;
   var i4add = i4.add;
   var i4and = i4.and;
   var i4ext = i4.extractLane;
+  var i4sel = i4.select;
   var f4add = f4.add;
   var f4sub = f4.sub;
   var f4mul = f4.mul;
   var f4lessThanOrEqual = f4.lessThanOrEqual;
   var f4splat = f4.splat;
   var imul = global.Math.imul;
-  const one4 = i4(1,1,1,1), two4 = f4(2,2,2,2), four4 = f4(4,4,4,4);
+  var b4 = global.SIMD.Bool32x4;
+  var b4any = b4.anyTrue;
+  const zero4 = i4(0,0,0,0), one4 = i4(1,1,1,1), two4 = f4(2,2,2,2), four4 = f4(4,4,4,4);
 
   const mk0 = 0x007fffff;
 
   function declareHeapLength() {
     b8[0x00ffffff] = 0;
   }
 
   function mapColorAndSetPixel (x, y, width, value, max_iterations) {
@@ -80,37 +83,37 @@ var moduleCode = `
     yd = toF(yd);
     max_iterations = max_iterations | 0;
     var c_re4  = f4(0,0,0,0), c_im4  = f4(0,0,0,0);
     var z_re4  = f4(0,0,0,0), z_im4  = f4(0,0,0,0);
     var count4 = i4(0,0,0,0);
     var z_re24 = f4(0,0,0,0), z_im24 = f4(0,0,0,0);
     var new_re4 = f4(0,0,0,0), new_im4 = f4(0,0,0,0);
     var i = 0;
-    var mi4 = i4(0,0,0,0);
+    var mb4 = b4(0,0,0,0);
 
     c_re4 = f4splat(xf);
     c_im4 = f4(yf, toF(yd + yf), toF(yd + toF(yd + yf)), toF(yd + toF(yd + toF(yd + yf))));
 
     z_re4  = c_re4;
     z_im4  = c_im4;
 
     for (i = 0; (i | 0) < (max_iterations | 0); i = (i + 1) | 0) {
       z_re24 = f4mul(z_re4, z_re4);
       z_im24 = f4mul(z_im4, z_im4);
-      mi4 = f4lessThanOrEqual(f4add(z_re24, z_im24), four4);
+      mb4 = f4lessThanOrEqual(f4add(z_re24, z_im24), four4);
       // If all 4 values are greater than 4.0, there's no reason to continue.
-      if ((mi4.signMask | 0) == 0x00)
+      if (!b4any(mb4))
         break;
 
       new_re4 = f4sub(z_re24, z_im24);
       new_im4 = f4mul(f4mul(two4, z_re4), z_im4);
       z_re4   = f4add(c_re4, new_re4);
       z_im4   = f4add(c_im4, new_im4);
-      count4  = i4add(count4, i4and(mi4, one4));
+      count4  = i4add(count4, i4sel(mb4, one4, zero4));
     }
     return ci4(count4);
   }
 
   function mandelColumnX4 (x, width, height, xf, yf, yd, max_iterations) {
     x = x | 0;
     width = width | 0;
     height = height | 0;
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -18,19 +18,26 @@ const I32S = 'var i4s = i4.sub;'
 const I32M = 'var i4m = i4.mul;'
 const F32 = 'var f4 = glob.SIMD.Float32x4;'
 const CF32 = 'var cf4 = f4.check;'
 const F32A = 'var f4a = f4.add;'
 const F32S = 'var f4s = f4.sub;'
 const F32M = 'var f4m = f4.mul;'
 const F32D = 'var f4d = f4.div;'
 const FROUND = 'var f32=glob.Math.fround;'
+const B32 = 'var b4 = glob.SIMD.Bool32x4;'
+const CB32 = 'var cb4 = b4.check;'
 
 const EXTI4 = 'var e = i4.extractLane;'
 const EXTF4 = 'var e = f4.extractLane;'
+const EXTB4 = 'var e = b4.extractLane;'
+
+// anyTrue / allTrue on boolean vectors.
+const ANYB4 = 'var anyt=b4.anyTrue;'
+const ALLB4 = 'var allt=b4.allTrue;'
 
 const INT32_MAX = Math.pow(2, 31) - 1;
 const INT32_MIN = INT32_MAX + 1 | 0;
 
 const assertEqFFI = {assertEq:assertEq};
 
 function CheckI4(header, code, expected) {
     // code needs to contain a local called x
@@ -41,16 +48,23 @@ function CheckI4(header, code, expected)
 
 function CheckF4(header, code, expected) {
     // code needs to contain a local called x
     header = USE_ASM + F32 + CF32 + EXTF4 + header;
     var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return cf4(x)} return f'), this)();
     assertEqX4(observed, expected.map(Math.fround));
 }
 
+function CheckB4(header, code, expected) {
+    // code needs to contain a local called x
+    header = USE_ASM + B32 + CB32 + header;
+    var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return cb4(x)} return f'), this)();
+    assertEqX4(observed, expected);
+}
+
 try {
 
 // 1. Constructors
 
 // 1.1 Compilation
 assertAsmTypeFail('glob', USE_ASM + "var i4 = Int32x4               ; return {}") ;
 assertAsmTypeFail('glob', USE_ASM + "var i4 = glob.Int32x4          ; return {}") ;
 assertAsmTypeFail('glob', USE_ASM + "var i4 = glob.globglob.Int32x4 ; return {}") ;
@@ -132,16 +146,18 @@ assertEq(asmLink(asmCompile('glob', USE_
 assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {f4(1,2,3,4);} return f"), this)(), undefined);
 
 // Int32x4 ctor should accept int?
 assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + CI32 + "var i32=new glob.Int32Array(heap); function f(i) {i=i|0; return ci4(i4(i32[i>>2], 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [0, 2, 3, 4]);
 // Float32x4 ctor should accept floatish (i.e. float || float? || floatish) and doublit
 assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + CF32 + FROUND + "var h=new glob.Float32Array(heap); function f(i) {i=i|0; return cf4(f4(h[i>>2], f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [NaN, 2, 3, 4]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + FROUND + "function f(i) {i=i|0; return cf4(f4(f32(1) + f32(2), f32(2), f32(3), f32(4)))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + FROUND + "function f(i) {i=i|0; return cf4(f4(f32(1) + f32(2), 2.0, 3.0, 4.0))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]);
+// Bool32x4 ctor should accept int?
+assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + B32 + CB32 + "var i32=new glob.Int32Array(heap); function f(i) {i=i|0; return cb4(b4(i32[i>>2], 2, 0, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [false, true, false, true]);
 
 // 1.3.2 Getters - Reading values out of lanes
 assertAsmTypeFail('glob', USE_ASM + I32 + EXTI4 + "function f() {var x=1; return e(x,1) | 0;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + EXTI4 + "function f() {var x=1; return e(x + x, 1) | 0;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + EXTI4 + "function f() {var x=1.; return e(x, 1) | 0;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + EXTF4 + "var f32=glob.Math.fround;" + I32 + "function f() {var x=f32(1); return e(x, 1) | 0;} return f");
 
 assertAsmTypeFail('glob', USE_ASM + I32 + EXTI4 + "function f() {var x=i4(1,2,3,4); return x.length|0;} return f");
@@ -157,61 +173,69 @@ assertAsmTypeFail('glob', USE_ASM + I32 
 
 // The signMask property is no longer supported. Replaced by allTrue / anyTrue.
 assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42; return x.signMask;} return f");
 assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42.; return x.signMask;} return f");
 assertAsmTypeFail('glob', USE_ASM + FROUND + "function f() {var x=f32(42.); return x.signMask;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + 'function f() { var x=i4(1,2,3,4); return x.signMask | 0 } return f');
 assertAsmTypeFail('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(1), f32(-13.37), f32(42), f32(-Infinity)); return x.signMask | 0 } return f');
 
-// signMask
-function CheckSignMask(innerBody, type, expected) {
+// Check lane extraction.
+function CheckLanes(innerBody, type, expected) {
     var coerceBefore, coerceAfter, extractLane;
 
     if (type === SIMD.Int32x4) {
         coerceBefore = '';
         coerceAfter = '|0';
         extractLane = 'ei';
     } else if (type === SIMD.Float32x4) {
         coerceBefore = '+';
         coerceAfter = '';
         extractLane = 'ef';
         expected = expected.map(Math.fround);
-    } else throw "unexpected type in CheckSignMask";
+    } else if (type === SIMD.Bool32x4) {
+        coerceBefore = '';
+        coerceAfter = '|0';
+        extractLane = 'eb';
+    } else throw "unexpected type in CheckLanes";
 
     for (var i = 0; i < 4; i++) {
         var lane = i;
         var laneCheckCode = `"use asm";
             var i4=glob.SIMD.Int32x4;
             var f4=glob.SIMD.Float32x4;
+            var b4=glob.SIMD.Bool32x4;
             var ei=i4.extractLane;
             var ef=f4.extractLane;
+            var eb=b4.extractLane;
             function f() {${innerBody}; return ${coerceBefore}${extractLane}(x, ${lane})${coerceAfter} }
             return f;`;
         assertEq(asmLink(asmCompile('glob', laneCheckCode), this)(), expected[i]);
     }
 }
-function CheckSignMaskI4(innerBody, expected) { return CheckSignMask(innerBody, SIMD.Int32x4, expected); }
-function CheckSignMaskF4(innerBody, expected) { return CheckSignMask(innerBody, SIMD.Float32x4, expected); }
+function CheckLanesI4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Int32x4, expected); }
+function CheckLanesF4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Float32x4, expected); }
+function CheckLanesB4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Bool32x4, expected); }
 
-CheckSignMaskI4('var x=i4(0,0,0,0);', [0,0,0,0]);
-CheckSignMaskI4('var x=i4(1,2,3,4);', [1,2,3,4]);
-CheckSignMaskI4('var x=i4(' + INT32_MIN + ',2,3,' + INT32_MAX + ')', [INT32_MIN,2,3,INT32_MAX]);
-CheckSignMaskI4('var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
-CheckSignMaskI4('var a=1; var b=i4(9,8,7,6); var c=13.37; var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
-CheckSignMaskI4('var y=i4(5,6,7,8); var x=i4(1,2,3,4)', [1,2,3,4]);
+CheckLanesI4('var x=i4(0,0,0,0);', [0,0,0,0]);
+CheckLanesI4('var x=i4(1,2,3,4);', [1,2,3,4]);
+CheckLanesI4('var x=i4(' + INT32_MIN + ',2,3,' + INT32_MAX + ')', [INT32_MIN,2,3,INT32_MAX]);
+CheckLanesI4('var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
+CheckLanesI4('var a=1; var b=i4(9,8,7,6); var c=13.37; var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
+CheckLanesI4('var y=i4(5,6,7,8); var x=i4(1,2,3,4)', [1,2,3,4]);
 
-CheckSignMaskF4('var x=f4(' + INT32_MAX + ', 2, 3, ' + INT32_MIN + ')', [INT32_MAX, 2, 3, INT32_MIN]);
-CheckSignMaskF4('var x=f4(' + (INT32_MAX + 1) + ', 2, 3, 4)', [INT32_MAX + 1, 2, 3, 4]);
-CheckSignMaskF4('var x=f4(1.3, 2.4, 3.5, 98.76)', [1.3, 2.4, 3.5, 98.76]);
-CheckSignMaskF4('var x=f4(13.37, 2., 3., -0)', [13.37, 2, 3, -0]);
+CheckLanesF4('var x=f4(' + INT32_MAX + ', 2, 3, ' + INT32_MIN + ')', [INT32_MAX, 2, 3, INT32_MIN]);
+CheckLanesF4('var x=f4(' + (INT32_MAX + 1) + ', 2, 3, 4)', [INT32_MAX + 1, 2, 3, 4]);
+CheckLanesF4('var x=f4(1.3, 2.4, 3.5, 98.76)', [1.3, 2.4, 3.5, 98.76]);
+CheckLanesF4('var x=f4(13.37, 2., 3., -0)', [13.37, 2, 3, -0]);
 
-assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4); var y=0.0; y=x.signMask;} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4); return (x.signMask > (1>>>0)) | 0;} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f() {var x=i4(1,2,3,4); var y=f32(0.0); y=x.signMask;} return f");
+CheckLanesB4('var x=b4(0,0,0,0);', [0,0,0,0]);
+CheckLanesB4('var x=b4(0,1,0,0);', [0,1,0,0]);
+CheckLanesB4('var x=b4(0,2,0,0);', [0,1,0,0]);
+CheckLanesB4('var x=b4(-1,0,1,-1);', [1,0,1,1]);
 
 // 1.3.3. Variable assignments
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4();} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1.0, 2, 3, 4);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2.0, 3, 4);} return f");
@@ -251,16 +275,17 @@ CheckI4('', 'var x=i4(1,2,3,4); var c=0;
 CheckF4(FROUND, 'var x=f4(1,2,3,4); var y=f32(0); var z=f32(0); y=e(x,2); z=e(x,2); x=f4(y,z,y,z)', [3, 3, 3, 3]);
 
 // Uses in ternary conditionals
 assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4; c=x?c:c;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4; x=1?x:c;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4; x=1?c:x;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?x:y;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
+assertAsmTypeFail('glob', USE_ASM + B32 + I32 + "function f() {var x=b4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
 
 CheckF4('', 'var x=f4(1,2,3,4); var y=f4(4,3,2,1); x=3?y:x', [4, 3, 2, 1]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=x|0; var v=f4(1,2,3,4); var w=f4(5,6,7,8); return cf4(x?w:v);} return f"), this)(1), [5,6,7,8]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(v) {v=cf4(v); var w=f4(5,6,7,8); return cf4(4?w:v);} return f"), this)(SIMD.Float32x4(1,2,3,4)), [5,6,7,8]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(v, x) {v=cf4(v); x=x|0; var w=f4(5,6,7,8); return cf4(x?w:v);} return f"), this)(SIMD.Float32x4(1,2,3,4), 0), [1,2,3,4]);
 
 CheckI4('', 'var x=i4(1,2,3,4); var y=i4(4,3,2,1); x=e(x,0)?y:x', [4, 3, 2, 1]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=x|0; var v=i4(1,2,3,4); var w=i4(5,6,7,8); return ci4(x?w:v);} return f"), this)(1), [5,6,7,8]);
@@ -270,16 +295,17 @@ assertEqX4(asmLink(asmCompile('glob', US
 // 1.3.4 Return values
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x)} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x + x)} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1.; return ci4(x)} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + "function f() {var x=f32(1.); return ci4(x)} return f");
 
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f() {var x=i4(1,2,3,4); return ci4(x)} return f"), this)(), [1,2,3,4]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f() {var x=f4(1,2,3,4); return cf4(x)} return f"), this)(), [1,2,3,4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + "function f() {var x=b4(1,2,0,4); return cb4(x)} return f"), this)(), [true,true,false,true]);
 
 // 1.3.5 Coerce and pass arguments
 // Via check
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4();} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); ci4(x, x);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4(1);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {ci4(1.);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + "function f() {ci4(f32(1.));} return f");
@@ -297,16 +323,20 @@ assertAsmTypeFail('glob', USE_ASM + F32 
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + FROUND + "function f() {cf4(f32(1.));} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + F32 + CF32 + "function f(x) {x=cf4(x); cf4(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return 1 + cf4(x) | 0;} return f");
 
 var f32x4 = SIMD.Float32x4(13.37, 42.42, -0, NaN);
 assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x)} return f"), this)(f32x4), undefined);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return cf4(x);} return f"), this)(f32x4), [13.37, 42.42, -0, NaN].map(Math.fround));
 
+var b32x4 = SIMD.Bool32x4(true, false, false, true);
+assertEq(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + "function f(x) {x=cb4(x)} return f"), this)(b32x4), undefined);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + "function f(x) {x=cb4(x); return cb4(x);} return f"), this)(b32x4), [true, false, false, true]);
+
 // Legacy coercions
 assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4();} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(1,2,3,4);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x,y) {x=i4(y);y=+y} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return i4(x);} return f");
 
 assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {return +i4(1,2,3,4)} return f");
@@ -329,23 +359,33 @@ function assertCaught(f) {
 }
 
 var f = asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=cf4(x); return cf4(x);} return f"), this);
 assertCaught(f);
 assertCaught(f, 1);
 assertCaught(f, {});
 assertCaught(f, "I sincerely am a SIMD typed object.");
 assertCaught(f, SIMD.Int32x4(1,2,3,4));
+assertCaught(f, SIMD.Bool32x4(true, true, false, true));
 
 var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=ci4(x); return ci4(x);} return f"), this);
 assertCaught(f);
 assertCaught(f, 1);
 assertCaught(f, {});
 assertCaught(f, "I sincerely am a SIMD typed object.");
 assertCaught(f, SIMD.Float32x4(4,3,2,1));
+assertCaught(f, SIMD.Bool32x4(true, true, false, true));
+
+var f = asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + "function f(x) {x=cb4(x); return cb4(x);} return f"), this);
+assertCaught(f);
+assertCaught(f, 1);
+assertCaught(f, {});
+assertCaught(f, "I sincerely am a SIMD typed object.");
+assertCaught(f, SIMD.Int32x4(1,2,3,4));
+assertCaught(f, SIMD.Float32x4(4,3,2,1));
 
 // 1.3.6 Globals
 // 1.3.6.1 Local globals
 // Read
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4; x=g|0;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4.; x=+g;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); var f32=glob.Math.fround; function f() {var x=f32(4.); x=f32(g);} return f");
 
@@ -366,35 +406,40 @@ assertAsmTypeFail('glob', USE_ASM + F32 
 
 CheckI4('var x=i4(1,2,3,4)', '', [1, 2, 3, 4]);
 CheckI4('var _=42; var h=i4(5,5,5,5); var __=13.37; var x=i4(4,7,9,2);', '', [4,7,9,2]);
 
 CheckF4('var x=f4(1.,2.,3.,4.)', '', [1, 2, 3, 4]);
 CheckF4('var _=42; var h=f4(5.,5.,5.,5.); var __=13.37; var x=f4(4.,13.37,9.,-0.);', '', [4, 13.37, 9, -0]);
 CheckF4('var x=f4(1,2,3,4)', '', [1, 2, 3, 4]);
 
+CheckB4('var x=b4(1,0,3,0)', '', [true, false, true, false]);
+CheckB4('var _=42; var h=b4(5,0,5,5); var __=13.37; var x=b4(0,0,9,2);', '', [false, false, true, true]);
+
 // Write
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4; g=x|0;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4.; g=+x;} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f");
 
 assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4; g=x|0;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; g=+x;} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f");
 
 assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CI32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=ci4(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CF32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=cf4(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=cf4(x);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=ci4(x);} return f");
 
 CheckI4('var x=i4(0,0,0,0);', 'x=i4(1,2,3,4)', [1,2,3,4]);
 CheckF4('var x=f4(0.,0.,0.,0.);', 'x=f4(5.,3.,4.,2.)', [5,3,4,2]);
+CheckB4('var x=b4(0,0,0,0);', 'x=b4(0,0,1,1)', [false, false, true, true]);
 
 CheckI4('var x=i4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=i4(1,2,3,4); y=24; z=4.9; w=23.10;', [1,2,3,4]);
 CheckF4('var x=f4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=f4(1,2,3,4); y=24; z=4.9; w=23.10;', [1,2,3,4]);
+CheckB4('var x=b4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=b4(1,0,0,0); y=24; z=4.9; w=23.10;', [true, false, false, false]);
 
 // 1.3.6.2 Imported globals
 // Read
 var Int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {return ci4(g)} return f"), this, {g: SIMD.Int32x4(1,2,3,4)})();
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 0), 1);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 1), 2);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 2), 3);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 3), 4);
@@ -406,29 +451,44 @@ var Float32x4 = asmLink(asmCompile('glob
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 0), 1);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 1), 2);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 2), 3);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 3), 4);
 
 for (var v of [1, {}, "totally legit SIMD variable", SIMD.Int32x4(1,2,3,4)])
     assertCaught(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {return cf4(g)} return f"), this, {g: v});
 
+var Bool32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + "var g=cb4(ffi.g); function f() {return cb4(g)} return f"), this, {g: SIMD.Bool32x4(false, false, false, true)})();
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 0), false);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 1), false);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 2), false);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 3), true);
+
+for (var v of [1, {}, "totally legit SIMD variable", SIMD.Int32x4(1,2,3,4)])
+    assertCaught(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + "var g=cb4(ffi.g); function f() {return cb4(g)} return f"), this, {g: v});
+
 // Write
 var Int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {g=i4(4,5,6,7); return ci4(g)} return f"), this, {g: SIMD.Int32x4(1,2,3,4)})();
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 0), 4);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 1), 5);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 2), 6);
 assertEq(SIMD.Int32x4.extractLane(Int32x4, 3), 7);
 
 var Float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {g=f4(4.,5.,6.,7.); return cf4(g)} return f"), this, {g: SIMD.Float32x4(1,2,3,4)})();
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 0), 4);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 1), 5);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 2), 6);
 assertEq(SIMD.Float32x4.extractLane(Float32x4, 3), 7);
 
+var Bool32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + "var g=cb4(ffi.g); function f() {g=b4(1,1,0,0); return cb4(g)} return f"), this, {g: SIMD.Bool32x4(1,1,1,0)})();
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 0), true);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 1), true);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 2), false);
+assertEq(SIMD.Bool32x4.extractLane(Bool32x4, 3), false);
+
 // 2. SIMD operations
 // 2.1 Compilation
 assertAsmTypeFail('glob', USE_ASM + "var add = Int32x4.add; return {}");
 assertAsmTypeFail('glob', USE_ASM + I32A + I32 + "return {}");
 assertAsmTypeFail('glob', USE_ASM + "var g = 3; var add = g.add; return {}");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var func = i4.doTheHarlemShake; return {}");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var div = i4.div; return {}");
 assertAsmTypeFail('glob', USE_ASM + "var f32 = glob.Math.fround; var i4a = f32.add; return {}");
@@ -555,19 +615,32 @@ function CheckUnaryF4(op, checkFunc, ass
 function CheckUnaryI4(op, checkFunc) {
     var _ = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + 'var op=i4.' + op + '; function f(x){x=ci4(x); return ci4(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}));
     }
 }
 
+function CheckUnaryB4(op, checkFunc) {
+    var _ = asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + 'var op=b4.' + op + '; function f(x){x=cb4(x); return cb4(op(x)); } return f'), this);
+    return function(input) {
+        var simd = SIMD.Bool32x4(input[0], input[1], input[2], input[3]);
+        assertEqX4(_(simd), input.map(checkFunc).map(function(x) { return !!x}));
+    }
+}
+
 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 CheckNotB = CheckUnaryB4('not', function(x) { return !x });
+CheckNotB([true, false, true, true]);
+CheckNotB([true, true, true, true]);
+CheckNotB([false, false, false, false]);
+
 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]);
 
@@ -653,91 +726,106 @@ CheckF4(RLF + FROUND, 'var x = f4(1,2,3,
 CheckF4(RLF + FROUND, 'var x = f4(1,2,3,4); x = r(x, 3, 13.37);', [1, 2, 3, Math.fround(13.37)]);
 
 const RLI = 'var r = i4.replaceLane;';
 CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 0, 42);', [42, 2, 3, 4]);
 CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 1, 42);', [1, 42, 3, 4]);
 CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 2, 42);', [1, 2, 42, 4]);
 CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 3, 42);', [1, 2, 3, 42]);
 
+const RLB = 'var r = b4.replaceLane;';
+CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 0, 0);', [false, true, false, false]);
+CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 1, 0);', [true, false, false, false]);
+CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 2, 2);', [true, true, true, false]);
+CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 3, 1);', [true, true, false, true]);
+
 // Comparisons
-// True yields all bits set to 1 (i.e as an int32, 0xFFFFFFFF === -1), false
-// yields all bits set to 0 (i.e 0).
-const T = -1;
-const F = 0;
+// Comparison operators produce Bool32x4 vectors.
+const T = true;
+const F = false;
 
 const EQI32 = 'var eq = i4.equal';
 const NEI32 = 'var ne = i4.notEqual';
 const LTI32 = 'var lt = i4.lessThan;';
 const LEI32 = 'var le = i4.lessThanOrEqual';
 const GTI32 = 'var gt = i4.greaterThan;';
 const GEI32 = 'var ge = i4.greaterThanOrEqual';
 
-CheckI4(EQI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=eq(x,y)', [F, F, F, F]);
-CheckI4(EQI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=eq(x,y)', [F, F, F, F]);
-CheckI4(EQI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=eq(x,y)', [T, F, F, F]);
+CheckB4(I32+EQI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=eq(a,b)', [F, F, F, F]);
+CheckB4(I32+EQI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=eq(a,b)', [F, F, F, F]);
+CheckB4(I32+EQI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=eq(a,b)', [T, F, F, F]);
 
-CheckI4(NEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=ne(x,y)', [T, T, T, T]);
-CheckI4(NEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=ne(x,y)', [T, T, T, T]);
-CheckI4(NEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=ne(x,y)', [F, T, T, T]);
+CheckB4(I32+NEI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=ne(a,b)', [T, T, T, T]);
+CheckB4(I32+NEI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=ne(a,b)', [T, T, T, T]);
+CheckB4(I32+NEI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=ne(a,b)', [F, T, T, T]);
 
-CheckI4(LTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=lt(x,y)', [F, F, F, F]);
-CheckI4(LTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=lt(x,y)', [T, T, T, T]);
-CheckI4(LTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=lt(x,y)', [F, T, T, F]);
+CheckB4(I32+LTI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=lt(a,b)', [F, F, F, F]);
+CheckB4(I32+LTI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=lt(a,b)', [T, T, T, T]);
+CheckB4(I32+LTI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=lt(a,b)', [F, T, T, F]);
 
-CheckI4(LEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=le(x,y)', [F, F, F, F]);
-CheckI4(LEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=le(x,y)', [T, T, T, T]);
-CheckI4(LEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=le(x,y)', [T, T, T, F]);
+CheckB4(I32+LEI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=le(a,b)', [F, F, F, F]);
+CheckB4(I32+LEI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=le(a,b)', [T, T, T, T]);
+CheckB4(I32+LEI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=le(a,b)', [T, T, T, F]);
 
-CheckI4(GTI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=gt(x,y)', [T, T, T, T]);
-CheckI4(GTI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=gt(x,y)', [F, F, F, F]);
-CheckI4(GTI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=gt(x,y)', [F, F, F, T]);
+CheckB4(I32+GTI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=gt(a,b)', [T, T, T, T]);
+CheckB4(I32+GTI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=gt(a,b)', [F, F, F, F]);
+CheckB4(I32+GTI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=gt(a,b)', [F, F, F, T]);
 
-CheckI4(GEI32, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=ge(x,y)', [T, T, T, T]);
-CheckI4(GEI32, 'var x=i4(-1,1,0,2); var y=i4(1,2,3,4); x=ge(x,y)', [F, F, F, F]);
-CheckI4(GEI32, 'var x=i4(1,0,3,4); var y=i4(1,1,7,0); x=ge(x,y)', [T, F, F, T]);
+CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4);  var b=i4(-1,1,0,2); x=ge(a,b)', [T, T, T, T]);
+CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4);  x=ge(a,b)', [F, F, F, F]);
+CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4);  var b=i4(1,1,7,0);  x=ge(a,b)', [T, F, F, T]);
 
 const LTF32 = 'var lt=f4.lessThan;';
 const LEF32 = 'var le=f4.lessThanOrEqual;';
 const GTF32 = 'var gt=f4.greaterThan;';
 const GEF32 = 'var ge=f4.greaterThanOrEqual;';
 const EQF32 = 'var eq=f4.equal;';
 const NEF32 = 'var ne=f4.notEqual;';
 
 assertAsmTypeFail('glob', USE_ASM + F32 + "var lt=f4.lessThan; function f() {var x=f4(1,2,3,4); var y=f4(5,6,7,8); x=lt(x,y);} return f");
 
-CheckI4(LTF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=lt(y,z)', [F, F, F, F]);
-CheckI4(LTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=lt(y,z)', [T, T, T, T]);
-CheckI4(LTF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=lt(y,z)', [F, T, T, F]);
-CheckI4(LTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=lt(y,z);', [F, F, F, F]);
+CheckB4(F32+LTF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=lt(y,z)', [F, F, F, F]);
+CheckB4(F32+LTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=lt(y,z)', [T, T, T, T]);
+CheckB4(F32+LTF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=lt(y,z)', [F, T, T, F]);
+CheckB4(F32+LTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=lt(y,z);', [F, F, F, F]);
+
+CheckB4(F32+LEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=le(y,z)', [F, F, F, F]);
+CheckB4(F32+LEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=le(y,z)', [T, T, T, T]);
+CheckB4(F32+LEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=le(y,z)', [T, T, T, F]);
+CheckB4(F32+LEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=le(y,z);', [T, T, F, F]);
 
-CheckI4(LEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=le(y,z)', [F, F, F, F]);
-CheckI4(LEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=le(y,z)', [T, T, T, T]);
-CheckI4(LEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=le(y,z)', [T, T, T, F]);
-CheckI4(LEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=le(y,z);', [T, T, F, F]);
+CheckB4(F32+EQF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
+CheckB4(F32+EQF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
+CheckB4(F32+EQF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=eq(y,z)', [T, F, F, F]);
+CheckB4(F32+EQF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=eq(y,z);', [T, T, F, F]);
 
-CheckI4(EQF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
-CheckI4(EQF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=eq(y,z)', [F, F, F, F]);
-CheckI4(EQF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=eq(y,z)', [T, F, F, F]);
-CheckI4(EQF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=eq(y,z);', [T, T, F, F]);
+CheckB4(F32+NEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
+CheckB4(F32+NEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
+CheckB4(F32+NEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=ne(y,z)', [F, T, T, T]);
+CheckB4(F32+NEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ne(y,z);', [F, F, T, T]);
 
-CheckI4(NEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
-CheckI4(NEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=ne(y,z)', [T, T, T, T]);
-CheckI4(NEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=ne(y,z)', [F, T, T, T]);
-CheckI4(NEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ne(y,z);', [F, F, T, T]);
+CheckB4(F32+GTF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=gt(y,z)', [T, T, T, T]);
+CheckB4(F32+GTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=gt(y,z)', [F, F, F, F]);
+CheckB4(F32+GTF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=gt(y,z)', [F, F, F, T]);
+CheckB4(F32+GTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=gt(y,z);', [F, F, F, F]);
+
+CheckB4(F32+GEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=b4(0,0,0,0); x=ge(y,z)', [T, T, T, T]);
+CheckB4(F32+GEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=b4(0,0,0,0); x=ge(y,z)', [F, F, F, F]);
+CheckB4(F32+GEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=b4(0,0,0,0); x=ge(y,z)', [T, F, F, T]);
+CheckB4(F32+GEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=b4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ge(y,z);', [T, T, F, F]);
 
-CheckI4(GTF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=gt(y,z)', [T, T, T, T]);
-CheckI4(GTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, F]);
-CheckI4(GTF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, T]);
-CheckI4(GTF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=gt(y,z);', [F, F, F, F]);
+var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LTI32 + B32 + ANYB4 + 'function f(x){x=ci4(x); var y=i4(-1,0,4,5); var b=b4(0,0,0,0); b=lt(x,y); return anyt(b)|0;} return f'), this);
+assertEq(f(SIMD.Int32x4(1,2,3,4)), 1);
+assertEq(f(SIMD.Int32x4(1,2,4,5)), 0);
+assertEq(f(SIMD.Int32x4(1,2,3,5)), 1);
 
-CheckI4(GEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=ge(y,z)', [T, T, T, T]);
-CheckI4(GEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=ge(y,z)', [F, F, F, F]);
-CheckI4(GEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=ge(y,z)', [T, F, F, T]);
-CheckI4(GEF32 + 'const nan = glob.NaN; const fround=glob.Math.fround', 'var y=f4(0,0,0,0); var z=f4(0,0,0,0); var x=i4(0,0,0,0); y=f4(fround(0.0),fround(-0.0),fround(0.0),fround(nan)); z=f4(fround(-0.0),fround(0.0),fround(nan),fround(0.0)); x=ge(y,z);', [T, T, F, F]);
+var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LTI32 + B32 + ALLB4 + 'function f(x){x=ci4(x); var y=i4(-1,0,4,5); var b=b4(0,0,0,0); b=lt(x,y); return allt(b)|0;} return f'), this);
+assertEq(f(SIMD.Int32x4(-2,-2,3,4)), 1);
+assertEq(f(SIMD.Int32x4(1,2,4,5)), 0);
+assertEq(f(SIMD.Int32x4(1,2,3,5)), 0);
 
 // Conversions operators
 const CVTIF = 'var cvt=f4.fromInt32x4;';
 const CVTFI = 'var cvt=i4.fromFloat32x4;';
 
 assertAsmTypeFail('glob', USE_ASM + I32 + "var cvt=i4.fromInt32x4; return {}");
 assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
@@ -810,16 +898,24 @@ assertEqX4(f(SIMD.Float32x4(-0,NaN,+Infi
 const ANDI32 = 'var andd=i4.and;';
 const ORI32 = 'var orr=i4.or;';
 const XORI32 = 'var xorr=i4.xor;';
 
 CheckI4(ANDI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=andd(x,y)', [42 & 2, 1337 & 4, -1 & 7, 13 & 15]);
 CheckI4(ORI32, ' var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=orr(x,y)',  [42 | 2, 1337 | 4, -1 | 7, 13 | 15]);
 CheckI4(XORI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=xorr(x,y)', [42 ^ 2, 1337 ^ 4, -1 ^ 7, 13 ^ 15]);
 
+const ANDB32 = 'var andd=b4.and;';
+const ORB32 = 'var orr=b4.or;';
+const XORB32 = 'var xorr=b4.xor;';
+
+CheckB4(ANDB32, 'var x=b4(1,0,1,0); var y=b4(1,1,0,0); x=andd(x,y)', [true, false, false, false]);
+CheckB4(ORB32, ' var x=b4(1,0,1,0); var y=b4(1,1,0,0); x=orr(x,y)',  [true, true, true, false]);
+CheckB4(XORB32, 'var x=b4(1,0,1,0); var y=b4(1,1,0,0); x=xorr(x,y)', [false, true, true, false]);
+
 // No bitwise ops on Float32x4.
 const ANDF32 = 'var andd=f4.and;';
 const ORF32 = 'var orr=f4.or;';
 const XORF32 = 'var xorr=f4.xor;';
 const NOTF32 = 'var nott=f4.not;';
 
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + ANDF32 + 'function f() {var x=f4(42, 13.37,-1.42, 23.10); var y=f4(19.89, 2.4, 8.15, 16.36); x=andd(x,y);} return f');
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + ORF32 + 'function f() {var x=f4(42, 13.37,-1.42, 23.10); var y=f4(19.89, 2.4, 8.15, 16.36); x=orr(x,y);} return f');
@@ -858,92 +954,60 @@ for (var i = 1; i < 64; i++) {
     assertEqX4(asmLsh(i, 3),  vinput.map(Lsh(i + 3)));
     assertEqX4(asmRsh(i, 3),  vinput.map(Rsh(i + 3)));
     assertEqX4(asmUrsh(i, 3), vinput.map(Ursh(i + 3)));
 }
 
 // Select
 const I32SEL = 'var i4sel = i4.select;'
 const F32SEL = 'var f4sel = f4.select;'
-const I32BSEL = 'var i4sel = i4.selectBits;'
 
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var x=f4(1,2,3,4); return ci4(i4sel(x,x,x));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
-assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var x=f4(1,2,3,4); return ci4(i4sel(x,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,2,3,4); var x=f4(1,2,3,4); return ci4(i4sel(m,x,x));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,2,3,4); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,2,3,4); var x=i4(1,2,3,4); var y=b4(5,6,7,8); return ci4(i4sel(m,x,y));} return f");
 
 assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); return cf4(f4sel(x,x,x));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=i4(1,2,3,4); return cf4(f4sel(m,x,x));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=f4(1,2,3,4); var x=f4(1,2,3,4); return cf4(f4sel(m,x,x));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=f4(1,2,3,4); var y=i4(5,6,7,8); return cf4(f4sel(m,x,y));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,2,3,4); var x=i4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y));} return f");
 
-// These pass with select but not selectBits
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(1,-1,2,-2); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(42,45,-42,-47); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
-
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(-1,-2,-3,-42); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(1,-1,2,-2); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(42,45,-42,-47); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
-
-// These pass for both select and selectBits
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
-
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + CI32 + I32SEL + "function f() {var m=b4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + CI32 + I32SEL + "function f() {var m=b4(1,1,1,1); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + CI32 + I32SEL + "function f() {var m=b4(0,1,0,1); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + CI32 + I32SEL + "function f() {var m=b4(0,0,1,1); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
 
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0,0,0); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0xffffffff,0xffffffff,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
-assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=i4(1,2,3,4); var y=i4(5,6,7,8); return ci4(i4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
-
-// Specific selectBits tests
-var masks = [
-    SIMD.Int32x4(1337, 0x1337, 0x42, 42),
-    SIMD.Int32x4(0x00FF1CE, 0xBAADF00D, 0xDEADBEEF, 0xCAFED00D),
-    SIMD.Int32x4(0xD15EA5E, 0xDEADC0DE, 0xFACEB00C, 0x4B1D4B1D)
-];
-
-var inputs = [
-    [SIMD.Int32x4(0,4,9,16), SIMD.Int32x4(1,2,3,4)],
-    [SIMD.Int32x4(-1, 2, INT32_MAX, INT32_MIN), SIMD.Int32x4(INT32_MAX, -4, INT32_MIN, 42)]
-];
-
-var i32bsel = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32BSEL + "function f(mask, ifTrue, ifFalse) {mask=ci4(mask); ifTrue=ci4(ifTrue); ifFalse=ci4(ifFalse); return ci4(i4sel(mask,ifTrue,ifFalse)); } return f"), this)
-
-for (var mask of masks) {
-    for (var [x, y] of inputs) {
-        assertEqX4(i32bsel(mask, x, y), simdToArray(SIMD.Int32x4.selectBits(mask, x, y)));
-    }
-}
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(0,0,0,0); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 7, 8]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(1,1,1,1); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(0,1,0,1); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(0,0,1,1); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
 
 // Splat
 const I32SPLAT = 'var splat=i4.splat;'
 const F32SPLAT = 'var splat=f4.splat;'
+const B32SPLAT = 'var splat=b4.splat;'
 
 assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); var p=f4(1.,2.,3.,4.); p=splat(f32(1));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1, 2)} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat()} return f");
 
 assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(m);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1.0);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + FROUND + "function f() {var m=i4(1,2,3,4); m=splat(f32(1.0));} return f");
 
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SPLAT + 'function f(){return ci4(splat(42));} return f'), this)(), [42, 42, 42, 42]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + B32SPLAT + 'function f(){return cb4(splat(42));} return f'), this)(), [true, true, true, true]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + B32SPLAT + 'function f(){return cb4(splat(0));} return f'), this)(), [false, false, false, false]);
 
 const l33t = Math.fround(13.37);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1)));} return f'), this)(), [1, 1, 1, 1]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(1.0));} return f'), this)(), [1, 1, 1, 1]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1 >>> 0)));} return f'), this)(), [1, 1, 1, 1]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(13.37)));} return f'), this)(), [l33t, l33t, l33t, l33t]);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(13.37));} return f'), this)(), [l33t, l33t, l33t, l33t]);
 
@@ -1065,20 +1129,22 @@ assertAsmTypeFail('glob', USE_ASM + I32 
 assertAsmTypeFail('glob', USE_ASM + I32 + "var sin=glob.Math.sin; function f() {var x=i4(1,2,3,4); x=i4(sin(3.0));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var ceil=glob.Math.sin; function f() {var x=i4(1,2,3,4); x=i4(ceil(3.0));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var pow=glob.Math.pow; function f() {var x=i4(1,2,3,4); x=i4(pow(1.0, 2.0));} return f");
 
 // 3.2. FFI calls
 // Can't pass SIMD arguments to FFI
 assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); func(x);} return f");
 assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); func(x);} return f");
+assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + "var func=ffi.func; function f() {var x=b4(1,2,3,4); func(x);} return f");
 
 // Can't have FFI return SIMD values
 assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); x=i4(func());} return f");
 assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); x=f4(func());} return f");
+assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + "var func=ffi.func; function f() {var x=b4(1,2,3,4); x=b4(func());} return f");
 
 // 3.3 Internal calls
 // asm.js -> asm.js
 // Retrieving values from asm.js
 var code = USE_ASM + I32 + CI32 + I32A + EXTI4 + `
     var check = ffi.check;
 
     function g() {
@@ -1145,16 +1211,34 @@ for (var i = 1; i < 10; ++i) {
     var c = code.replace(/\[args\]/g, args)
                 .replace(/\[last\]/g, last)
                 .replace(/\[decls\]/i, decls)
                 .replace(/\[coerc\]/i, coerc)
                 .replace(/\[i\]/g, i);
     asmLink(asmCompile('glob', 'ffi', c), this, ffi)();
 }
 
+// Passing boolean results to extern functions.
+// Verify that these functions are typed correctly.
+function isone(x) { return (x===1)|0 }
+var f = asmLink(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + ANYB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(anyt(i)|0)|0; } return f'), this, {isone:isone});
+assertEq(f(SIMD.Bool32x4(0,0,1,0)), 1)
+assertEq(f(SIMD.Bool32x4(0,0,0,0)), 0)
+assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + CB32 + ANYB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(anyt(i))|0; } return f');
+
+var f = asmLink(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + ALLB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(allt(i)|0)|0; } return f'), this, {isone:isone});
+assertEq(f(SIMD.Bool32x4(1,1,1,1)), 1)
+assertEq(f(SIMD.Bool32x4(0,1,0,0)), 0)
+assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + CB32 + ALLB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(allt(i))|0; } return f');
+
+var f = asmLink(asmCompile('glob', 'ffi', USE_ASM + B32 + CB32 + EXTB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(e(i,2)|0)|0; } return f'), this, {isone:isone});
+assertEq(f(SIMD.Bool32x4(1,1,1,1)), 1)
+assertEq(f(SIMD.Bool32x4(0,1,0,0)), 0)
+assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + CB32 + EXTB4 + 'var isone=ffi.isone; function f(i) { i=cb4(i); return isone(e(i,2))|0; } return f');
+
 // Stress-test for register spilling code and stack depth checks
 var code = `
     "use asm";
     var i4 = glob.SIMD.Int32x4;
     var i4a = i4.add;
     var e = i4.extractLane;
     var assertEq = ffi.assertEq;
     function g() {
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -177,35 +177,35 @@ function testSimdX4(ctor, shift, scale, 
         assertEq(t1, r1);
         if (!t) assertEqX4(v, l);
         if (!t3) assertEqX4(v3, l3);
         if (!t2) assertEqX4(v2, l2);
         if (!t1) assertEqX4(v1, l1);
 
         // Stores
         if (!t) {
-            simdCtor.store(arr, index, simdCtor.not(v));
+            simdCtor.store(arr, index, simdCtor.neg(v));
             f.store(i, v);
             assertEqX4(simdCtor.load(arr, index), v);
         } else
             assertThrowsInstanceOf(() => f.store(i, simdCtor()), RangeError);
         if (!t3) {
-            simdCtor.store3(arr, index, simdCtor.not(v3));
+            simdCtor.store3(arr, index, simdCtor.neg(v3));
             f.store3(i, v3);
             assertEqX4(simdCtor.load3(arr, index), v3);
         } else
             assertThrowsInstanceOf(() => f.store3(i, simdCtor()), RangeError);
         if (!t2) {
-            simdCtor.store2(arr, index, simdCtor.not(v2));
+            simdCtor.store2(arr, index, simdCtor.neg(v2));
             f.store2(i, v2);
             assertEqX4(simdCtor.load2(arr, index), v2);
         } else
             assertThrowsInstanceOf(() => f.store2(i, simdCtor()), RangeError);
         if (!t1) {
-            simdCtor.store1(arr, index, simdCtor.not(v1));
+            simdCtor.store1(arr, index, simdCtor.neg(v1));
             f.store1(i, v1);
             assertEqX4(simdCtor.load1(arr, index), v1);
         } else
             assertThrowsInstanceOf(() => f.store1(i, simdCtor()), RangeError);
     }
 }
 
 function testFloat32x4(ctor, shift, scale, disp) {