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.
This commit is contained in:
Jakob Stoklund Olesen 2015-12-22 14:17:13 -08:00
parent 9e32ee152d
commit 7f13078252
11 changed files with 653 additions and 174 deletions

View File

@ -105,7 +105,7 @@ GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, Mut
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
@ -148,6 +148,8 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSModule& module, AsmJSModule::Gl
*(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:
@ -195,6 +197,14 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSModule& module, AsmJSModule::Gl
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;
}
@ -307,6 +317,7 @@ 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");
}
@ -317,6 +328,7 @@ 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");
}
@ -376,6 +388,7 @@ ValidateSimdOperation(JSContext* cx, AsmJSModule::Global& global, HandleValue gl
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()) {
@ -393,9 +406,18 @@ ValidateSimdOperation(JSContext* cx, AsmJSModule::Global& global, HandleValue gl
"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))
@ -717,6 +739,14 @@ CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
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;
}
}
}
@ -784,6 +814,12 @@ CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
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;

View File

@ -73,7 +73,8 @@ enum AsmJSAtomicsBuiltinFunction
enum AsmJSSimdType
{
AsmJSSimdType_int32x4,
AsmJSSimdType_float32x4
AsmJSSimdType_float32x4,
AsmJSSimdType_bool32x4
};
// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
@ -871,9 +872,19 @@ class AsmJSModule
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);
}

View File

@ -532,6 +532,7 @@ class NumLit
Float,
Int32x4,
Float32x4,
Bool32x4,
OutOfRangeInt = -1
};
@ -584,7 +585,7 @@ class NumLit
}
bool isSimd() const {
return which_ == Int32x4 || which_ == Float32x4;
return which_ == Int32x4 || which_ == Float32x4 || which_ == Bool32x4;
}
const jit::SimdConstant& simdValue() const {
@ -610,6 +611,8 @@ class NumLit
return ValType::I32x4;
case NumLit::Float32x4:
return ValType::F32x4;
case NumLit::Bool32x4:
return ValType::B32x4;
case NumLit::OutOfRangeInt:;
}
MOZ_CRASH("bad literal");
@ -629,6 +632,8 @@ class NumLit
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");
@ -647,6 +652,7 @@ class Type
Float = NumLit::Float,
Int32x4 = NumLit::Int32x4,
Float32x4 = NumLit::Float32x4,
Bool32x4 = NumLit::Bool32x4,
Double,
MaybeDouble,
MaybeFloat,
@ -670,6 +676,9 @@ class Type
case AsmJSSimdType_float32x4:
which_ = Float32x4;
return;
case AsmJSSimdType_bool32x4:
which_ = Bool32x4;
return;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad AsmJSSimdType");
}
@ -682,6 +691,7 @@ class Type
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");
}
@ -695,6 +705,7 @@ class Type
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");
}
@ -702,7 +713,7 @@ class 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;
@ -722,6 +733,7 @@ class Type
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();
@ -741,6 +753,7 @@ class Type
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");
}
@ -805,8 +818,12 @@ class Type
return which_ == Float32x4;
}
bool isBool32x4() const {
return which_ == Bool32x4;
}
bool isSimd() const {
return isInt32x4() || isFloat32x4();
return isInt32x4() || isFloat32x4() || isBool32x4();
}
bool isVarType() const {
@ -846,6 +863,8 @@ class Type
return jit::MIRType_Int32x4;
case Float32x4:
return jit::MIRType_Float32x4;
case Bool32x4:
return jit::MIRType_Bool32x4;
case Void:
return jit::MIRType_None;
}
@ -859,6 +878,8 @@ class Type
return AsmJSSimdType_int32x4;
case Float32x4:
return AsmJSSimdType_float32x4;
case Bool32x4:
return AsmJSSimdType_bool32x4;
// Scalar types
case Double:
case DoubleLit:
@ -892,6 +913,7 @@ class Type
case Intish: return "intish";
case Int32x4: return "int32x4";
case Float32x4: return "float32x4";
case Bool32x4: return "bool32x4";
case Void: return "void";
}
MOZ_CRASH("Invalid Type");
@ -1791,6 +1813,9 @@ IsCoercionCall(ModuleValidator& m, ParseNode* pn, ValType* coerceTo, ParseNode**
case AsmJSSimdType_float32x4:
*coerceTo = ValType::F32x4;
return true;
case AsmJSSimdType_bool32x4:
*coerceTo = ValType::B32x4;
return true;
}
}
@ -1814,8 +1839,9 @@ static unsigned
SimdTypeToLength(AsmJSSimdType type)
{
switch (type) {
case AsmJSSimdType_float32x4:
case AsmJSSimdType_int32x4:
case AsmJSSimdType_float32x4:
case AsmJSSimdType_bool32x4:
return 4;
}
MOZ_CRASH("unexpected SIMD type");
@ -1863,6 +1889,7 @@ IsSimdLiteral(ModuleValidator& m, ParseNode* pn)
uint32_t _;
switch (type) {
case AsmJSSimdType_int32x4:
case AsmJSSimdType_bool32x4:
if (!IsLiteralInt(m, arg, &_))
return false;
case AsmJSSimdType_float32x4:
@ -1933,6 +1960,17 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
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.");
@ -2001,6 +2039,7 @@ IsLiteralInt(NumLit lit, uint32_t* u32)
case NumLit::OutOfRangeInt:
case NumLit::Int32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return false;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
@ -2226,6 +2265,11 @@ class MOZ_STACK_CLASS FunctionValidator
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;
}
@ -2553,6 +2597,10 @@ IsSimdTypeName(ModuleValidator& m, PropertyName* name, AsmJSSimdType* type)
*type = AsmJSSimdType_float32x4;
return true;
}
if (name == m.cx()->names().bool32x4) {
*type = AsmJSSimdType_bool32x4;
return true;
}
return false;
}
@ -2573,6 +2621,12 @@ IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
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");
@ -2950,6 +3004,7 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
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);
@ -2970,6 +3025,7 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
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");
}
@ -3303,6 +3359,7 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
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);
@ -3325,6 +3382,7 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
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");
}
@ -3872,6 +3930,7 @@ CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calle
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
@ -3966,6 +4025,7 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type*
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
@ -4034,6 +4094,7 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT
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
@ -4119,6 +4180,11 @@ CheckCoercionArg(FunctionValidator& f, ParseNode* arg, ValType expected, Type* t
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");
@ -4286,6 +4352,7 @@ SimdToCoercedScalarType(AsmJSSimdType t)
{
switch (t) {
case AsmJSSimdType_int32x4:
case AsmJSSimdType_bool32x4:
return Type::Intish;
case AsmJSSimdType_float32x4:
return Type::Floatish;
@ -4325,6 +4392,7 @@ class CheckSimdScalarArgs
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;
}
@ -4343,9 +4411,9 @@ class CheckSimdSelectArgs
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;
}
@ -4381,6 +4449,7 @@ class CheckSimdVectorScalarArgs
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");
@ -4442,6 +4511,7 @@ class CheckSimdReplaceLaneArgs
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:
@ -4463,11 +4533,12 @@ class CheckSimdReplaceLaneArgs
} // 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");
}
@ -4476,7 +4547,7 @@ 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;
@ -4500,7 +4571,7 @@ 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);
}
@ -4508,7 +4579,7 @@ 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);
}
@ -4517,13 +4588,19 @@ 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;
}
@ -4551,6 +4628,10 @@ CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType
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));
}
@ -4558,7 +4639,7 @@ CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, AsmJSSimdType 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;
@ -4576,7 +4657,8 @@ CheckSimdCast(FunctionValidator& f, ParseNode* call, AsmJSSimdType fromType, Asm
{
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;
@ -4606,7 +4688,7 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty
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);
@ -4634,7 +4716,7 @@ CheckSimdShuffle(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, Ty
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);
@ -4678,6 +4760,7 @@ CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call, AsmJSSimdType opTy
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);
@ -4718,7 +4801,7 @@ CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
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);
@ -4743,7 +4826,7 @@ CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
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);
@ -4771,13 +4854,47 @@ CheckSimdStore(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
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)
{
@ -4791,7 +4908,7 @@ CheckSimdCheck(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType, 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;
@ -4900,8 +5017,9 @@ CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValida
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");
}
@ -4913,7 +5031,7 @@ CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::
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)))
@ -4964,6 +5082,8 @@ CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actu
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
@ -5002,6 +5122,11 @@ CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actu
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);
@ -5034,7 +5159,6 @@ CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidato
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);
@ -5249,6 +5373,8 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
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");
@ -5296,6 +5422,9 @@ CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
} 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",
@ -5324,6 +5453,7 @@ IsValidIntMultiplyConstant(ModuleValidator& m, ParseNode* expr)
case NumLit::OutOfRangeInt:
case NumLit::Int32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return false;
}
@ -5692,6 +5822,8 @@ CheckAsExprStatement(FunctionValidator& f, ParseNode* expr)
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");
@ -5897,6 +6029,7 @@ CheckCaseExpr(FunctionValidator& f, ParseNode* caseExpr, int32_t* value)
case NumLit::Float:
case NumLit::Int32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return f.fail(caseExpr, "switch case expression must be an integer literal");
}
@ -6075,6 +6208,8 @@ CheckReturn(FunctionValidator& f, ParseNode* returnStmt)
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

View File

@ -43,13 +43,20 @@ enum class ValType
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
@ -62,6 +69,7 @@ ToMIRType(ValType vt)
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");
}
@ -97,7 +105,11 @@ class Val
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_; }
@ -107,7 +119,10 @@ class Val
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_; }
};
@ -125,6 +140,7 @@ enum class ExprType : uint8_t
F64 = uint8_t(ValType::F64),
I32x4 = uint8_t(ValType::I32x4),
F32x4 = uint8_t(ValType::F32x4),
B32x4 = uint8_t(ValType::B32x4),
Void
};

View File

@ -64,6 +64,7 @@ enum class Stmt : uint8_t
F64Expr,
I32X4Expr,
F32X4Expr,
B32X4Expr,
Id,
Noop,
@ -173,6 +174,9 @@ enum class I32 : uint8_t
// SIMD opcodes
I32X4ExtractLane,
B32X4ExtractLane,
B32X4AllTrue,
B32X4AnyTrue,
// Specific to AsmJS
Id,
@ -312,8 +316,6 @@ enum class I32X4 : uint8_t
Unary,
Binary,
BinaryCompI32X4,
BinaryCompF32X4,
BinaryBitwise,
BinaryShift,
@ -377,6 +379,43 @@ enum class F32X4 : uint8_t
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,

View File

@ -129,6 +129,10 @@ class FunctionCompiler
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);
@ -315,7 +319,6 @@ class FunctionCompiler
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);
@ -323,6 +326,26 @@ class FunctionCompiler
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)
{
@ -1290,6 +1313,12 @@ EmitLiteral(FunctionCompiler& f, ValType type, MDefinition**def)
*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");
}
@ -1317,7 +1346,9 @@ 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)
@ -1526,6 +1557,7 @@ EmitCallArgs(FunctionCompiler& f, const LifoSig& sig, FunctionCompiler::Call* ca
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;
@ -1734,6 +1766,7 @@ 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:
@ -1789,8 +1822,12 @@ EmitSimdReplaceLane(FunctionCompiler& f, ValType simdType, MDefinition** def)
}
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;
}
@ -1878,10 +1915,36 @@ EmitSimdStore(FunctionCompiler& f, ValType type, MDefinition** def)
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;
*def = f.selectSimd(defs[0], defs[1], defs[2], ToMIRType(type));
if (!EmitExpr(f, type, &defs[0]) || !EmitExpr(f, type, &defs[1]))
return false;
*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;
}
@ -1895,6 +1958,16 @@ EmitSimdSplat(FunctionCompiler& f, ValType type, MDefinition** def)
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)
{
@ -1917,6 +1990,15 @@ EmitSimdCtor(FunctionCompiler& f, ValType type, MDefinition** def)
*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:
@ -2177,10 +2259,24 @@ EmitExpr(FunctionCompiler& f, ValType type, MDefinition** 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)
{
@ -2480,6 +2576,7 @@ EmitStatement(FunctionCompiler& f, Stmt stmt, LabelVector* maybeLabels /*= nullp
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, &_);
@ -2623,6 +2720,12 @@ EmitI32Expr(FunctionCompiler& f, MDefinition** def)
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;
}
@ -2810,10 +2913,6 @@ EmitI32X4Expr(FunctionCompiler& f, MDefinition** def)
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:
@ -2897,6 +2996,55 @@ EmitF32X4Expr(FunctionCompiler& f, MDefinition** def)
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)
{

View File

@ -190,6 +190,7 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
"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:
@ -222,6 +223,7 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
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()));
@ -269,6 +271,7 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
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;
@ -549,6 +552,7 @@ GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex
break;
case ExprType::I32x4:
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}
@ -800,6 +804,7 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
break;
case ExprType::I32x4:
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
}

View File

@ -58,6 +58,7 @@ var code = `
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;
@ -68,6 +69,7 @@ var code = `
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);
@ -95,7 +97,7 @@ var code = `
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;
@ -122,7 +124,7 @@ var code = `
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);

View File

@ -34,13 +34,16 @@ var moduleCode = `
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;
@ -85,7 +88,7 @@ var moduleCode = `
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))));
@ -96,16 +99,16 @@ var moduleCode = `
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);
}

View File

@ -23,9 +23,16 @@ 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;
@ -46,6 +53,13 @@ function CheckF4(header, code, expected) {
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
@ -137,6 +151,8 @@ assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + CI32 + "var
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");
@ -162,8 +178,8 @@ assertAsmTypeFail('glob', USE_ASM + FROUND + "function f() {var x=f32(42.); retu
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) {
@ -175,38 +191,46 @@ function CheckSignMask(innerBody, type, expected) {
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");
@ -256,6 +280,7 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var
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]);
@ -275,6 +300,7 @@ assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + "function f() {var x=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
@ -302,6 +328,10 @@ 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");
@ -334,6 +364,7 @@ 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);
@ -341,6 +372,15 @@ 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
@ -371,6 +411,9 @@ 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");
@ -387,9 +430,11 @@ assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CI32 + "var g=i4(1,2,3,4); funct
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
@ -411,6 +456,15 @@ 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);
@ -424,6 +478,12 @@ 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 {}");
@ -560,9 +620,22 @@ function CheckUnaryI4(op, checkFunc) {
}
}
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]);
@ -658,11 +731,16 @@ 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';
@ -671,29 +749,29 @@ 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;';
@ -704,35 +782,45 @@ 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]);
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+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(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+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(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+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(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]);
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]);
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]);
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]);
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);
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;';
@ -815,6 +903,14 @@ CheckI4(ANDI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=andd(x,y)', [
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;';
@ -863,15 +959,17 @@ for (var i = 1; i < 64; i++) {
// 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");
@ -879,56 +977,20 @@ assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + F32SEL + "function f() {v
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 + 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 + 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 + 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");
@ -939,6 +1001,8 @@ assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2
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]);
@ -1070,10 +1134,12 @@ assertAsmTypeFail('glob', USE_ASM + I32 + "var pow=glob.Math.pow; function f() {
// 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
@ -1150,6 +1216,24 @@ for (var i = 1; i < 10; ++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";

View File

@ -182,25 +182,25 @@ function testSimdX4(ctor, shift, scale, disp, simdName, simdCtor) {
// 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