From 61109135a9c9bf9e47475282e6b20a6d679b6f8b Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 15 Sep 2014 14:46:35 +0200 Subject: [PATCH] Bug 1062217: Generalize SIMD operation and ctor validation; r=luke --- js/src/asmjs/AsmJSValidate.cpp | 350 ++++++++++++++++----------------- 1 file changed, 175 insertions(+), 175 deletions(-) diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index c33dcdfba4db..70daa31832da 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -558,6 +558,30 @@ class Type MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type"); } + Type simdToCoercedScalarType() const { + JS_ASSERT(isSimd()); + switch (which_) { + case Int32x4: + return Intish; + case Float32x4: + return Floatish; + // Scalar types + case Double: + case MaybeDouble: + case Float: + case MaybeFloat: + case Floatish: + case Fixnum: + case Int: + case Signed: + case Unsigned: + case Intish: + case Void: + break; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type"); + } + AsmJSSimdType simdToSimdType() const { JS_ASSERT(isSimd()); switch (which_) { @@ -4718,161 +4742,127 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinF return true; } +typedef Vector DefinitionVector; + +template static bool -CheckUnarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, - MDefinition **def, Type *type) +CheckSimdCallArgs(FunctionCompiler &f, ParseNode *call, unsigned expectedArity, + const CheckArgOp &checkArg, DefinitionVector *defs) { unsigned numArgs = CallArgListLength(call); - if (numArgs != 1) - return f.failf(call, "expected 1 argument to unary arithmetic SIMD operation, got %u", numArgs); + if (numArgs != expectedArity) + return f.failf(call, "expected %u arguments to SIMD call, got %u", expectedArity, numArgs); + + DefinitionVector &argDefs = *defs; + if (!argDefs.resize(numArgs)) + return false; ParseNode *arg = CallArgList(call); - MDefinition *argDef; - Type argType; - if (!CheckExpr(f, arg, &argDef, &argType)) - return false; + for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) { + MOZ_ASSERT(!!arg); - // For now, the only unary SIMD operation is splat(scalar). - MOZ_ASSERT(global->simdOperation() == AsmJSSimdOperation_splat); - switch (global->simdOperationType()) { - case AsmJSSimdType_int32x4: - if (!argType.isIntish()) - return f.failf(arg, "%s is not a subtype of intish", argType.toChars()); - break; - case AsmJSSimdType_float32x4: - if (!CheckFloatCoercionArg(f, arg, argType, argDef, &argDef)) - return false; - break; - } - - *type = global->simdOperationType(); - *def = f.splatSimd(argDef, type->toMIRType()); - return true; -} - -static bool -CheckBinarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, - MDefinition **def, Type *type) -{ - unsigned numArgs = CallArgListLength(call); - if (numArgs != 2) - return f.failf(call, "expected 2 arguments to binary arithmetic SIMD operation, got %u", numArgs); - - ParseNode *lhs = CallArgList(call); - ParseNode *rhs = NextNode(lhs); - - MDefinition *lhsDef, *rhsDef; - Type lhsType, rhsType; - if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) - return false; - if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) - return false; - - Type retType = global->simdOperationType(); - if (lhsType != retType || rhsType != retType) - return f.failf(lhs, "arguments to SIMD binary op should both be %s", retType.toChars()); - - MIRType opType = retType.toMIRType(); - switch (global->simdOperation()) { - case AsmJSSimdOperation_add: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryArith::Add, opType); - *type = retType; - break; - case AsmJSSimdOperation_sub: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryArith::Sub, opType); - *type = retType; - break; - case AsmJSSimdOperation_mul: - JS_ASSERT(!retType.isInt32x4()); - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryArith::Mul, opType); - *type = retType; - break; - case AsmJSSimdOperation_div: - JS_ASSERT(!retType.isInt32x4()); - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryArith::Div, opType); - *type = retType; - break; - case AsmJSSimdOperation_lessThan: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::lessThan); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_lessThanOrEqual: - JS_ASSERT(!retType.isInt32x4()); - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::lessThanOrEqual); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_equal: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::equal); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_notEqual: - JS_ASSERT(!retType.isInt32x4()); - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::notEqual); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_greaterThan: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::greaterThan); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_greaterThanOrEqual: - JS_ASSERT(!retType.isInt32x4()); - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryComp::greaterThanOrEqual); - *type = Type::Int32x4; - break; - case AsmJSSimdOperation_and: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryBitwise::and_, opType); - *type = retType; - break; - case AsmJSSimdOperation_or: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryBitwise::or_, opType); - *type = retType; - break; - case AsmJSSimdOperation_xor: - *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryBitwise::xor_, opType); - *type = retType; - break; - case AsmJSSimdOperation_splat: - case AsmJSSimdOperation_select: - MOZ_CRASH("unexpected SIMD binary operation"); + Type argType; + if (!CheckExpr(f, arg, &argDefs[i], &argType)) + return false; + if (!checkArg(f, arg, i, argType, &argDefs[i])) + return false; } return true; } -static bool -CheckSimdSelect(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, - MDefinition **def, Type *type) +namespace { + +class CheckSimdScalarArgs { - MOZ_ASSERT(global->simdOperation() == AsmJSSimdOperation_select); + Type formalType_; - unsigned numArgs = CallArgListLength(call); - if (numArgs != 3) - return f.failf(call, "expected 3 arguments to ternary SIMD operation, got %u", numArgs); + public: + explicit CheckSimdScalarArgs(Type t) : formalType_(t.simdToCoercedScalarType()) {} - ParseNode *mask = CallArgList(call); - ParseNode *lhs = NextNode(mask); - ParseNode *rhs = NextNode(lhs); + bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType, + MDefinition **argDef) const + { + if (formalType_ == Type::Floatish) + return CheckFloatCoercionArg(f, arg, actualType, *argDef, argDef); - MDefinition *maskDef; - Type maskType; - if (!CheckExpr(f, mask, &maskDef, &maskType)) + if (!(actualType <= formalType_)) { + return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(), + formalType_.toChars()); + } + return true; + } +}; + +class CheckSimdVectorArgs +{ + Type formalType_; + + public: + explicit CheckSimdVectorArgs(Type t) : formalType_(t) {} + + bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType, + MDefinition **argDef) const + { + if (!(actualType <= formalType_)) { + return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(), + formalType_.toChars()); + } + return true; + } +}; + +class CheckSimdSelectArgs +{ + Type formalType_; + + public: + explicit CheckSimdSelectArgs(Type t) : formalType_(t) {} + + bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType, + MDefinition **argDef) 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()); + return true; + } + + if (!(actualType <= formalType_)) { + return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(), + formalType_.toChars()); + } + return true; + } +}; + +} // anonymous namespace + +template +static inline bool +CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, OpEnum op, MDefinition **def, + Type *type) +{ + DefinitionVector argDefs; + if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorArgs(retType), &argDefs)) return false; - if (maskType != Type::Int32x4) - return f.failf(mask, "%s is not a subtype of int32x4", maskType.toChars()); - - MDefinition *lhsDef, *rhsDef; - Type lhsType, rhsType; - if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) - return false; - if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) - return false; - - Type retType = global->simdOperationType(); - if (lhsType != retType || rhsType != retType) - return f.failf(mask, "last two arguments to SIMD ternary op should both be %s", retType.toChars()); - + *def = f.binarySimd(argDefs[0], argDefs[1], op, retType.toMIRType()); *type = retType; - *def = f.ternarySimd(maskDef, lhsDef, rhsDef, MSimdTernaryBitwise::select, retType.toMIRType()); + return true; +} + +template<> +inline bool +CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, + MSimdBinaryComp::Operation op, MDefinition **def, + Type *type) +{ + DefinitionVector argDefs; + if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorArgs(retType), &argDefs)) + return false; + *def = f.binarySimd(argDefs[0], argDefs[1], op); + *type = Type::Int32x4; return true; } @@ -4880,26 +4870,58 @@ static bool CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, MDefinition **def, Type *type) { - JS_ASSERT(global->isSimdOperation()); + MOZ_ASSERT(global->isSimdOperation()); + + Type retType = global->simdOperationType(); + switch (global->simdOperation()) { - case AsmJSSimdOperation_splat: - return CheckUnarySimd(f, call, global, def, type); case AsmJSSimdOperation_add: + return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Add, def, type); case AsmJSSimdOperation_sub: + return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Sub, def, type); case AsmJSSimdOperation_mul: + return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Mul, def, type); case AsmJSSimdOperation_div: + return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Div, def, type); + case AsmJSSimdOperation_lessThan: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThan, def, type); case AsmJSSimdOperation_lessThanOrEqual: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThanOrEqual, def, type); case AsmJSSimdOperation_equal: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::equal, def, type); case AsmJSSimdOperation_notEqual: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::notEqual, def, type); case AsmJSSimdOperation_greaterThan: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThan, def, type); case AsmJSSimdOperation_greaterThanOrEqual: + return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThanOrEqual, def, type); + case AsmJSSimdOperation_and: + return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::and_, def, type); case AsmJSSimdOperation_or: + return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::or_, def, type); case AsmJSSimdOperation_xor: - return CheckBinarySimd(f, call, global, def, type); - case AsmJSSimdOperation_select: - return CheckSimdSelect(f, call, global, def, type); + return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type); + + case AsmJSSimdOperation_splat: { + DefinitionVector defs; + if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(retType), &defs)) + return false; + *def = f.splatSimd(defs[0], retType.toMIRType()); + *type = retType; + return true; + } + + case AsmJSSimdOperation_select: { + DefinitionVector defs; + if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(retType), &defs)) + return false; + *def = f.ternarySimd(defs[0], defs[1], defs[2], MSimdTernaryBitwise::select, + retType.toMIRType()); + *type = retType; + return true; + } } MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall"); } @@ -4916,44 +4938,22 @@ CheckSimdCtorCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Gl return CheckCoercionArg(f, argNode, coercion, def, type); AsmJSSimdType simdType = global->simdCtorType(); - unsigned numArgs = CallArgListLength(call); unsigned length = SimdTypeToLength(simdType); - if (numArgs != length) - return f.failName(call, "invalid number of arguments in call to '%s'", CallCallee(call)->name()); + Type retType = simdType; - Vector defs; - if (!defs.resize(length)) + DefinitionVector defs; + if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(retType), &defs)) return false; - argNode = CallArgList(call); - size_t i = 0; - for (; argNode; argNode = NextNode(argNode), ++i) - { - JS_ASSERT(i < length); - - Type argType; - if (!CheckExpr(f, argNode, &defs[i], &argType)) - return false; - - switch (simdType) { - case AsmJSSimdType_int32x4: - if (!argType.isIntish()) - return f.failf(argNode, "argument %d of Int32x4 ctor isn't a subtype of intish", i); - break; - case AsmJSSimdType_float32x4: - if (!CheckFloatCoercionArg(f, argNode, argType, defs[i], &defs[i])) - return false; - break; - } - } - JS_ASSERT(i == length); - - *type = simdType; + // This code will need to be generalized when we handle float64x2 + MOZ_ASSERT(length == 4); + MIRType opType = retType.toMIRType(); if (defs[1] == defs[0] && defs[2] == defs[0] && defs[3] == defs[0]) - *def = f.splatSimd(defs[0], type->toMIRType()); + *def = f.splatSimd(defs[0], opType); else - *def = f.constructSimd(defs[0], defs[1], defs[2], defs[3], type->toMIRType()); + *def = f.constructSimd(defs[0], defs[1], defs[2], defs[3], opType); + *type = retType; return true; }