Bug 904918: Odin Float32 support; p=bbouvier,dougc r=luke,sstangl

Authors:
- Douglas Crosher (dougc) for the ARM backend support
- Benjamin Bouvier (bbouvier) for everything else
This commit is contained in:
Benjamin Bouvier 2013-12-12 20:23:29 +01:00
parent fd36ed6219
commit 1b008d2a72
22 changed files with 654 additions and 275 deletions

View File

@ -270,7 +270,7 @@ private:
OP_GROUP2_Ev1 = 0xD1,
OP_GROUP2_EvCL = 0xD3,
OP_FPU6 = 0xDD,
OP_FLD32 = 0xD9,
OP_FPU6_F32 = 0xD9,
OP_CALL_rel32 = 0xE8,
OP_JMP_rel32 = 0xE9,
PRE_SSE_F2 = 0xF2,
@ -732,7 +732,7 @@ public:
void fld32_m(int offset, RegisterID base)
{
spew("fld %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FLD, base, offset);
m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FLD, base, offset);
}
void fisttp_m(int offset, RegisterID base)
{
@ -747,7 +747,7 @@ public:
void fstp32_m(int offset, RegisterID base)
{
spew("fstp32 %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FSTP, base, offset);
m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FSTP, base, offset);
}
void negl_r(RegisterID dst)

View File

@ -368,7 +368,10 @@ class Type
public:
enum Which {
Double,
Doublish,
MaybeDouble,
Float,
MaybeFloat,
Floatish,
Fixnum,
Int,
Signed,
@ -407,8 +410,20 @@ class Type
return which_ == Double;
}
bool isDoublish() const {
return isDouble() || which_ == Doublish;
bool isMaybeDouble() const {
return isDouble() || which_ == MaybeDouble;
}
bool isFloat() const {
return which_ == Float;
}
bool isMaybeFloat() const {
return isFloat() || which_ == MaybeFloat;
}
bool isFloatish() const {
return isMaybeFloat() || which_ == Floatish;
}
bool isVoid() const {
@ -420,14 +435,18 @@ class Type
}
bool isVarType() const {
return isInt() || isDouble();
return isInt() || isDouble() || isFloat();
}
MIRType toMIRType() const {
switch (which_) {
case Double:
case Doublish:
case MaybeDouble:
return MIRType_Double;
case Float:
case Floatish:
case MaybeFloat:
return MIRType_Float32;
case Fixnum:
case Int:
case Signed:
@ -442,14 +461,17 @@ class Type
const char *toChars() const {
switch (which_) {
case Double: return "double";
case Doublish: return "doublish";
case Fixnum: return "fixnum";
case Int: return "int";
case Signed: return "signed";
case Unsigned: return "unsigned";
case Intish: return "intish";
case Void: return "void";
case Double: return "double";
case MaybeDouble: return "double?";
case Float: return "float";
case Floatish: return "floatish";
case MaybeFloat: return "float?";
case Fixnum: return "fixnum";
case Int: return "int";
case Signed: return "signed";
case Unsigned: return "unsigned";
case Intish: return "intish";
case Void: return "void";
}
MOZ_ASSUME_UNREACHABLE("Invalid Type");
}
@ -465,7 +487,8 @@ class RetType
enum Which {
Void = Type::Void,
Signed = Type::Signed,
Double = Type::Double
Double = Type::Double,
Float = Type::Float
};
private:
@ -478,6 +501,7 @@ class RetType
switch (coercion) {
case AsmJS_ToInt32: which_ = Signed; break;
case AsmJS_ToNumber: which_ = Double; break;
case AsmJS_FRound: which_ = Float; break;
}
}
Which which() const {
@ -490,6 +514,7 @@ class RetType
switch (which_) {
case Void: return AsmJSModule::Return_Void;
case Signed: return AsmJSModule::Return_Int32;
case Float: // will be converted to a Double
case Double: return AsmJSModule::Return_Double;
}
MOZ_ASSUME_UNREACHABLE("Unexpected return type");
@ -499,6 +524,7 @@ class RetType
case Void: return MIRType_None;
case Signed: return MIRType_Int32;
case Double: return MIRType_Double;
case Float: return MIRType_Float32;
}
MOZ_ASSUME_UNREACHABLE("Unexpected return type");
}
@ -534,7 +560,8 @@ class VarType
public:
enum Which {
Int = Type::Int,
Double = Type::Double
Double = Type::Double,
Float = Type::Float
};
private:
@ -549,6 +576,7 @@ class VarType
switch (coercion) {
case AsmJS_ToInt32: which_ = Int; break;
case AsmJS_ToNumber: which_ = Double; break;
case AsmJS_FRound: which_ = Float; break;
}
}
Which which() const {
@ -558,18 +586,40 @@ class VarType
return Type::Which(which_);
}
MIRType toMIRType() const {
return which_ == Int ? MIRType_Int32 : MIRType_Double;
switch(which_) {
case Int: return MIRType_Int32;
case Double: return MIRType_Double;
case Float: return MIRType_Float32;
}
MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
return MIRType_None;
}
AsmJSCoercion toCoercion() const {
return which_ == Int ? AsmJS_ToInt32 : AsmJS_ToNumber;
switch(which_) {
case Int: return AsmJS_ToInt32;
case Double: return AsmJS_ToNumber;
case Float: return AsmJS_FRound;
}
MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
return AsmJS_ToInt32;
}
static VarType FromMIRType(MIRType type) {
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
return type == MIRType_Int32 ? Int : Double;
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32);
switch(type) {
case MIRType_Int32: return Int;
case MIRType_Float32: return Float;
case MIRType_Double: return Double;
default: MOZ_ASSUME_UNREACHABLE("FromMIRType MIR type not handled"); return Int;
}
}
static VarType FromCheckedType(Type type) {
JS_ASSERT(type.isInt() || type.isDoublish());
return type.isDoublish() ? Double : Int;
JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
if (type.isMaybeDouble())
return Double;
else if (type.isFloatish())
return Float;
else
return Int;
}
bool operator==(VarType rhs) const { return which_ == rhs.which_; }
bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
@ -584,6 +634,7 @@ operator<=(Type lhs, VarType rhs)
switch (rhs.which()) {
case VarType::Int: return lhs.isInt();
case VarType::Double: return lhs.isDouble();
case VarType::Float: return lhs.isFloat();
}
MOZ_ASSUME_UNREACHABLE("Unexpected rhs type");
}
@ -706,6 +757,8 @@ class NumLit
Value v_;
public:
NumLit() {}
NumLit(Which w, Value v)
: which_(w), v_(v)
{}
@ -792,6 +845,29 @@ ExtractNumericLiteral(ParseNode *pn)
return NumLit(NumLit::NegativeInt, Int32Value(i64));
}
static bool
ExtractFRoundableLiteral(ParseNode *pn, double *value)
{
if (!IsNumericLiteral(pn))
return false;
NumLit literal = ExtractNumericLiteral(pn);
switch (literal.which()) {
case NumLit::Double:
*value = literal.toDouble();
return true;
case NumLit::Fixnum:
case NumLit::NegativeInt:
case NumLit::BigUnsigned:
literal = NumLit(NumLit::Double, DoubleValue(literal.toInt32()));
*value = literal.toDouble();
return true;
case NumLit::OutOfRangeInt:
break;
}
return false;
}
static inline bool
IsLiteralInt(ParseNode *pn, uint32_t *u32)
{
@ -848,32 +924,9 @@ TypedArrayLoadType(ArrayBufferView::ViewType viewType)
case ArrayBufferView::TYPE_UINT32:
return Type::Intish;
case ArrayBufferView::TYPE_FLOAT32:
return Type::MaybeFloat;
case ArrayBufferView::TYPE_FLOAT64:
return Type::Doublish;
default:;
}
MOZ_ASSUME_UNREACHABLE("Unexpected array type");
}
enum ArrayStoreEnum {
ArrayStore_Intish,
ArrayStore_Doublish
};
static ArrayStoreEnum
TypedArrayStoreType(ArrayBufferView::ViewType viewType)
{
switch (viewType) {
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_INT16:
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT8:
case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_UINT32:
return ArrayStore_Intish;
case ArrayBufferView::TYPE_FLOAT32:
case ArrayBufferView::TYPE_FLOAT64:
return ArrayStore_Doublish;
return Type::MaybeDouble;
default:;
}
MOZ_ASSUME_UNREACHABLE("Unexpected array type");
@ -1305,7 +1358,8 @@ class MOZ_STACK_CLASS ModuleCompiler
!addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
!addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
!addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
!addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul))
!addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround))
{
return false;
}
@ -1441,7 +1495,7 @@ class MOZ_STACK_CLASS ModuleCompiler
bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v,
bool isConst) {
uint32_t index;
if (!module_->addGlobalVarInitConstant(v, &index))
if (!module_->addGlobalVarInitConstant(v, type.toCoercion(), &index))
return false;
Global *global = moduleLifo_.new_<Global>(Global::Variable);
if (!global)
@ -1808,9 +1862,16 @@ class FunctionCompiler
Local(VarType t, unsigned slot) : type(t), slot(slot) {}
};
struct TypedValue
{
VarType type;
Value value;
TypedValue(VarType t, const Value &v) : type(t), value(v) {}
};
private:
typedef HashMap<PropertyName*, Local> LocalMap;
typedef js::Vector<Value> VarInitializerVector;
typedef js::Vector<TypedValue> VarInitializerVector;
typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap;
typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap;
typedef js::Vector<ParseNode*, 4> NodeStack;
@ -1924,7 +1985,7 @@ class FunctionCompiler
return failName(pn, "duplicate local name '%s' not allowed", name);
if (!locals_.add(p, name, Local(type, locals_.count())))
return false;
return varInitializers_.append(init);
return varInitializers_.append(TypedValue(type, init));
}
bool prepareToEmitMIR(const VarTypeVector &argTypes)
@ -1950,7 +2011,8 @@ class FunctionCompiler
}
unsigned firstLocalSlot = argTypes.length();
for (unsigned i = 0; i < varInitializers_.length(); i++) {
MConstant *ins = MConstant::New(alloc(), varInitializers_[i]);
MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value,
varInitializers_[i].type.toMIRType());
curBlock_->add(ins);
curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
}
@ -2010,6 +2072,16 @@ class FunctionCompiler
return constant;
}
MDefinition *constantFloat(float f)
{
if (!curBlock_)
return NULL;
MConstant *constant = MConstant::NewAsmJS(alloc(), DoubleValue(double(f)), MIRType_Float32);
curBlock_->add(constant);
return constant;
}
template <class T>
MDefinition *unary(MDefinition *op)
{
@ -2869,10 +2941,39 @@ CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseN
return m.addGlobalVarInitConstant(varName, type, literal.value(), isConst);
}
static bool
CheckFloat32Coercion(ModuleCompiler &m, ParseNode *callNode, ParseNode **coercedExpr,
const char* errorMessage)
{
JS_ASSERT(callNode->isKind(PNK_CALL));
ParseNode *callee = CallCallee(callNode);
if (!callee->isKind(PNK_NAME))
return m.fail(callee, errorMessage);
PropertyName *calleeName = callee->name();
const ModuleCompiler::Global *global = m.lookupGlobal(calleeName);
if (!global || global->which() != ModuleCompiler::Global::MathBuiltin ||
global->mathBuiltin() != AsmJSMathBuiltin_fround)
{
return m.fail(callee, errorMessage);
}
unsigned numArgs = CallArgListLength(callNode);
if (numArgs != 1)
return m.failf(callee, "fround passed %u arguments, expected one", numArgs);
if (coercedExpr)
*coercedExpr = CallArgList(callNode);
return true;
}
static bool
CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
ParseNode **coercedExpr = nullptr)
{
static const char *errorMessage = "in coercion expression, the expression must be of the form +x, fround(x) or x|0";
switch (coercionNode->getKind()) {
case PNK_BITOR: {
ParseNode *rhs = BinaryRight(coercionNode);
@ -2895,21 +2996,20 @@ CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *c
*coercedExpr = UnaryKid(coercionNode);
return true;
}
case PNK_CALL: {
*coercion = AsmJS_FRound;
return CheckFloat32Coercion(m, coercionNode, coercedExpr, errorMessage);
}
default:;
}
return m.fail(coercionNode, "in coercion expression, the expression must be of the form +x or x|0");
return m.fail(coercionNode, errorMessage);
}
static bool
CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion,
ParseNode *coercedExpr, bool isConst)
{
AsmJSCoercion coercion;
ParseNode *coercedExpr;
if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
return false;
if (!coercedExpr->isKind(PNK_DOT))
return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
@ -2925,6 +3025,35 @@ CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNod
return m.addGlobalVarImport(varName, field, coercion, isConst);
}
static bool
CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
{
AsmJSCoercion coercion;
ParseNode *coercedExpr;
if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
return false;
return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst);
}
static bool
CheckGlobalVariableInitFloat32(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
{
ParseNode *arg = NULL;
if (!CheckFloat32Coercion(m, initNode, &arg, "call must be of the form fround(x)"))
return false;
if (IsNumericLiteral(arg)) {
double value;
if (!ExtractFRoundableLiteral(arg, &value))
return m.fail(arg, "float global initializer needs to be a double literal");
return m.addGlobalVarInitConstant(varName, VarType::Float, DoubleValue(value), isConst);
}
return CheckGlobalVariableImportExpr(m, varName, AsmJSCoercion::AsmJS_FRound, arg, isConst);
}
static bool
CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
{
@ -3027,6 +3156,9 @@ CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst)
if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS))
return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
if (initNode->isKind(PNK_CALL))
return CheckGlobalVariableInitFloat32(m, var->name(), initNode, isConst);
if (initNode->isKind(PNK_NEW))
return CheckNewArrayView(m, var->name(), initNode);
@ -3058,7 +3190,7 @@ static bool
ArgFail(FunctionCompiler &f, PropertyName *argName, ParseNode *stmt)
{
return f.failName(stmt, "expecting argument type declaration for '%s' of the "
"form 'arg = arg|0' or 'arg = +arg'", argName);
"form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName);
}
static bool
@ -3170,6 +3302,18 @@ CheckVariable(FunctionCompiler &f, ParseNode *var)
if (!initNode)
return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
if (initNode->isKind(PNK_CALL)) {
ParseNode *coercedVar = NULL;
if (!CheckFloat32Coercion(f.m(), initNode, &coercedVar, "caller in var initializer can only be fround"))
return false;
double value;
if (!ExtractFRoundableLiteral(coercedVar, &value))
return f.failName(coercedVar, "float initializer for '%s' needs to be a double literal", name);
return f.addVariable(var, name, VarType::Float, DoubleValue(value));
}
if (!IsNumericLiteral(initNode))
return f.failName(initNode, "initializer for '%s' needs to be a numeric literal", name);
@ -3416,7 +3560,7 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
}
static bool
CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
{
ArrayBufferView::ViewType viewType;
MDefinition *pointerDef;
@ -3443,15 +3587,30 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
return false;
switch (TypedArrayStoreType(viewType)) {
case ArrayStore_Intish:
switch (viewType) {
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_INT16:
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT8:
case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_UINT32:
if (!rhsType.isIntish())
return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
break;
case ArrayStore_Doublish:
if (!rhsType.isDoublish())
return f.failf(lhs, "%s is not a subtype of doublish", rhsType.toChars());
case ArrayBufferView::TYPE_FLOAT32:
if (rhsType.isMaybeDouble())
rhsDef = f.unary<MToFloat32>(rhsDef);
else if (!rhsType.isFloatish())
return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars());
break;
case ArrayBufferView::TYPE_FLOAT64:
if (rhsType.isFloat())
rhsDef = f.unary<MToDouble>(rhsDef);
else if (!rhsType.isMaybeDouble())
return f.failf(lhs, "%s is not a subtype of float or double?", rhsType.toChars());
break;
default:
MOZ_ASSUME_UNREACHABLE("Unexpected view type");
}
f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
@ -3564,7 +3723,7 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition
return true;
}
if (argType.isDoublish()) {
if (argType.isMaybeDouble()) {
if (retType != RetType::Double)
return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
*def = f.unary<MAbs>(argDef, MIRType_Double);
@ -3572,7 +3731,15 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition
return true;
}
return f.failf(call, "%s is not a subtype of signed or doublish", argType.toChars());
if (argType.isMaybeFloat()) {
if (retType != RetType::Float)
return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
*def = f.unary<MAbs>(argDef, MIRType_Float32);
*type = Type::Float;
return true;
}
return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
}
static bool
@ -3588,7 +3755,7 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition
if (!CheckExpr(f, arg, &argDef, &argType))
return false;
if (argType.isDoublish()) {
if (argType.isMaybeDouble()) {
if (retType != RetType::Double)
return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
*def = f.unary<MSqrt>(argDef, MIRType_Double);
@ -3596,7 +3763,15 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition
return true;
}
return f.failf(call, "%s is not a subtype of doublish", argType.toChars());
if (argType.isMaybeFloat()) {
if (retType != RetType::Float)
return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
*def = f.unary<MSqrt>(argDef, MIRType_Float32);
*type = Type::Float;
return true;
}
return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
}
typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
@ -3672,7 +3847,7 @@ static bool
CheckIsVarType(FunctionCompiler &f, ParseNode *argNode, Type type)
{
if (!type.isVarType())
return f.failf(argNode, "%s is not a subtype of int or double", type.toChars());
return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars());
return true;
}
@ -3786,6 +3961,9 @@ CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetTyp
{
PropertyName *calleeName = CallCallee(callNode)->name();
if (retType == RetType::Float)
return f.fail(callNode, "FFI calls can't return float");
FunctionCompiler::Call call(f, retType);
if (!CheckCallArgs(f, callNode, CheckIsExternType, &call))
return false;
@ -3801,11 +3979,88 @@ CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetTyp
return true;
}
static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type);
static bool
CheckIsDoublish(FunctionCompiler &f, ParseNode *argNode, Type type)
CheckFRoundArg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, const char* error)
{
if (!type.isDoublish())
return f.failf(argNode, "%s is not a subtype of doublish", type.toChars());
ParseNode *arg = NULL;
if (!CheckFloat32Coercion(f.m(), expr, &arg, error))
return false;
if (arg->isKind(PNK_CALL))
return CheckCall(f, arg, RetType::Float, def, type);
if (IsNumericLiteral(arg)) {
double value;
if (!ExtractFRoundableLiteral(arg, &value))
return f.fail(arg, "call to fround with literal expects the literal to be a double");
*def = f.constantFloat(value);
*type = Type::Float;
return true;
}
MDefinition *inputDef;
Type inputType;
if (!CheckExpr(f, arg, &inputDef, &inputType))
return false;
if (inputType.isMaybeDouble() || inputType.isSigned())
*def = f.unary<MToFloat32>(inputDef);
else if (inputType.isUnsigned())
*def = f.unary<MAsmJSUnsignedToFloat32>(inputDef);
else if (inputType.isFloatish())
*def = inputDef;
else
return f.failf(arg, "%s is not a subtype of signed, unsigned, double? or floatish", inputType.toChars());
*type = Type::Float;
return true;
}
static bool
CheckFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type)
{
MDefinition *operand;
Type operandType;
if (!CheckFRoundArg(f, callNode, &operand, &operandType, "coercion to float should use fround"))
return false;
switch (retType.which()) {
case RetType::Double:
*def = f.unary<MToDouble>(operand);
*type = Type::Double;
return true;
case RetType::Signed:
*def = f.unary<MTruncateToInt32>(operand);
*type = Type::Signed;
return true;
case RetType::Float:
*def = operand;
*type = Type::Float;
return true;
case RetType::Void:
// definition and return types should be ignored by the caller
return true;
}
MOZ_ASSUME_UNREACHABLE("return value of fround is ignored");
}
static bool
CheckIsMaybeDouble(FunctionCompiler &f, ParseNode *argNode, Type type)
{
if (!type.isMaybeDouble())
return f.failf(argNode, "%s is not a subtype of double?", type.toChars());
return true;
}
static bool
CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type)
{
if (!type.isMaybeFloat())
return f.failf(argNode, "%s is not a subtype of float?", type.toChars());
return true;
}
@ -3814,39 +4069,46 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin
RetType retType, MDefinition **def, Type *type)
{
unsigned arity = 0;
AsmJSImmKind callee;
AsmJSImmKind doubleCallee, floatCallee;
switch (mathBuiltin) {
case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type);
case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type);
case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type);
case AsmJSMathBuiltin_sin: arity = 1; callee = AsmJSImm_SinD; break;
case AsmJSMathBuiltin_cos: arity = 1; callee = AsmJSImm_CosD; break;
case AsmJSMathBuiltin_tan: arity = 1; callee = AsmJSImm_TanD; break;
case AsmJSMathBuiltin_asin: arity = 1; callee = AsmJSImm_ASinD; break;
case AsmJSMathBuiltin_acos: arity = 1; callee = AsmJSImm_ACosD; break;
case AsmJSMathBuiltin_atan: arity = 1; callee = AsmJSImm_ATanD; break;
case AsmJSMathBuiltin_ceil: arity = 1; callee = AsmJSImm_CeilD; break;
case AsmJSMathBuiltin_floor: arity = 1; callee = AsmJSImm_FloorD; break;
case AsmJSMathBuiltin_exp: arity = 1; callee = AsmJSImm_ExpD; break;
case AsmJSMathBuiltin_log: arity = 1; callee = AsmJSImm_LogD; break;
case AsmJSMathBuiltin_pow: arity = 2; callee = AsmJSImm_PowD; break;
case AsmJSMathBuiltin_atan2: arity = 2; callee = AsmJSImm_ATan2D; break;
case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type);
case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type);
case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type);
case AsmJSMathBuiltin_fround: return CheckFRound(f, callNode, retType, def, type);
case AsmJSMathBuiltin_sin: arity = 1; doubleCallee = AsmJSImm_SinD; floatCallee = AsmJSImm_SinF; break;
case AsmJSMathBuiltin_cos: arity = 1; doubleCallee = AsmJSImm_CosD; floatCallee = AsmJSImm_CosF; break;
case AsmJSMathBuiltin_tan: arity = 1; doubleCallee = AsmJSImm_TanD; floatCallee = AsmJSImm_TanF; break;
case AsmJSMathBuiltin_asin: arity = 1; doubleCallee = AsmJSImm_ASinD; floatCallee = AsmJSImm_ASinF; break;
case AsmJSMathBuiltin_acos: arity = 1; doubleCallee = AsmJSImm_ACosD; floatCallee = AsmJSImm_ACosF; break;
case AsmJSMathBuiltin_atan: arity = 1; doubleCallee = AsmJSImm_ATanD; floatCallee = AsmJSImm_ATanF; break;
case AsmJSMathBuiltin_ceil: arity = 1; doubleCallee = AsmJSImm_CeilD; floatCallee = AsmJSImm_CeilF; break;
case AsmJSMathBuiltin_floor: arity = 1; doubleCallee = AsmJSImm_FloorD; floatCallee = AsmJSImm_FloorF; break;
case AsmJSMathBuiltin_exp: arity = 1; doubleCallee = AsmJSImm_ExpD; floatCallee = AsmJSImm_ExpF; break;
case AsmJSMathBuiltin_log: arity = 1; doubleCallee = AsmJSImm_LogD; floatCallee = AsmJSImm_LogF; break;
case AsmJSMathBuiltin_pow: arity = 2; doubleCallee = AsmJSImm_PowD; floatCallee = AsmJSImm_Invalid; break;
case AsmJSMathBuiltin_atan2: arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break;
}
if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid)
return f.fail(callNode, "math builtin cannot be used as float");
if (retType != RetType::Double && retType != RetType::Float)
return f.failf(callNode, "return type of math function is double or float, used as %s", retType.toType().toChars());
FunctionCompiler::Call call(f, retType);
if (!CheckCallArgs(f, callNode, CheckIsDoublish, &call))
if (retType == RetType::Float && !CheckCallArgs(f, callNode, CheckIsMaybeFloat, &call))
return false;
if (retType == RetType::Double && !CheckCallArgs(f, callNode, CheckIsMaybeDouble, &call))
return false;
if (call.sig().args().length() != arity)
return f.failf(callNode, "call passed %u arguments, expected %u", call.sig().args().length(), arity);
if (!f.builtinCall(callee, call, MIRType_Double, def))
if (retType == RetType::Float && !f.builtinCall(floatCallee, call, retType.toMIRType(), def))
return false;
if (retType == RetType::Double && !f.builtinCall(doubleCallee, call, retType.toMIRType(), def))
return false;
if (retType != RetType::Double)
return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars());
*type = Type::Double;
*type = retType.toType();
return true;
}
@ -3898,14 +4160,14 @@ CheckPos(FunctionCompiler &f, ParseNode *pos, MDefinition **def, Type *type)
if (!CheckExpr(f, operand, &operandDef, &operandType))
return false;
if (operandType.isSigned())
if (operandType.isMaybeFloat() || operandType.isSigned())
*def = f.unary<MToDouble>(operandDef);
else if (operandType.isUnsigned())
*def = f.unary<MAsmJSUnsignedToDouble>(operandDef);
else if (operandType.isDoublish())
else if (operandType.isMaybeDouble())
*def = operandDef;
else
return f.failf(operand, "%s is not a subtype of signed, unsigned or doublish", operandType.toChars());
return f.failf(operand, "%s is not a subtype of signed, unsigned, float or double?", operandType.toChars());
*type = Type::Double;
return true;
@ -3947,13 +4209,19 @@ CheckNeg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
return true;
}
if (operandType.isDoublish()) {
if (operandType.isMaybeDouble()) {
*def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double);
*type = Type::Double;
return true;
}
return f.failf(operand, "%s is not a subtype of int or doublish", operandType.toChars());
if (operandType.isMaybeFloat()) {
*def = f.unary<MAsmJSNeg>(operandDef, MIRType_Float32);
*type = Type::Floatish;
return true;
}
return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
}
static bool
@ -3967,14 +4235,14 @@ CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *
if (!CheckExpr(f, operand, &operandDef, &operandType))
return false;
if (operandType.isDoublish()) {
if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
*def = f.unary<MTruncateToInt32>(operandDef);
*type = Type::Signed;
return true;
}
if (!operandType.isIntish())
return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars());
return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
*def = operandDef;
*type = Type::Signed;
@ -4071,6 +4339,8 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
*type = Type::Int;
} else if (thenType.isDouble() && elseType.isDouble()) {
*type = Type::Double;
} else if (thenType.isFloat() && elseType.isFloat()) {
*type = Type::Float;
} else {
return f.failf(ternary, "then/else branches of conditional must both produce int or double, "
"current types are %s and %s", thenType.toChars(), elseType.toChars());
@ -4130,14 +4400,19 @@ CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *typ
return true;
}
if (!lhsType.isDoublish())
return f.failf(lhs, "%s is not a subtype of doublish", lhsType.toChars());
if (!rhsType.isDoublish())
return f.failf(rhs, "%s is not a subtype of doublish", rhsType.toChars());
if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
*def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
*type = Type::Double;
return true;
}
*def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
*type = Type::Double;
return true;
if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
*def = f.mul(lhsDef, rhsDef, MIRType_Float32, MMul::Normal);
*type = Type::Floatish;
return true;
}
return f.fail(star, "multiply operands must be both int, both double? or both float?");
}
static bool
@ -4185,13 +4460,18 @@ CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
*type = Type::Intish;
} else if (lhsType.isDoublish() && rhsType.isDoublish()) {
} else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
*def = expr->isKind(PNK_ADD)
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
*type = Type::Double;
} else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
*def = expr->isKind(PNK_ADD)
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Float32)
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Float32);
*type = Type::Floatish;
} else {
return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
lhsType.toChars(), rhsType.toChars());
}
@ -4214,7 +4494,7 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
return false;
if (lhsType.isDoublish() && rhsType.isDoublish()) {
if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
*def = expr->isKind(PNK_DIV)
? f.div(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false)
: f.mod(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false);
@ -4222,6 +4502,15 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
return true;
}
if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
if (expr->isKind(PNK_DIV))
*def = f.div(lhsDef, rhsDef, MIRType_Float32, /* unsignd = */ false);
else
return f.fail(expr, "modulo cannot receive float arguments");
*type = Type::Floatish;
return true;
}
if (lhsType.isSigned() && rhsType.isSigned()) {
if (expr->isKind(PNK_DIV))
*def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false);
@ -4240,7 +4529,7 @@ CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
return true;
}
return f.failf(expr, "arguments to / or %% must both be double, signed, or unsigned; "
return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
"%s and %s are given", lhsType.toChars(), rhsType.toChars());
}
@ -4274,7 +4563,13 @@ CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *t
return true;
}
return f.failf(comp, "arguments to a comparison must both be signed, unsigned or doubles; "
if (lhsType.isFloat() && rhsType.isFloat()) {
*def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Float32);
*type = Type::Int;
return true;
}
return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
"%s and %s are given", lhsType.toChars(), rhsType.toChars());
}
@ -4345,6 +4640,18 @@ CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *t
return true;
}
static bool
CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
{
static const char* callError = "all function calls must either be ignored (via "
"f(); or comma-expression), coerced to signed "
"(via f()|0), coerced to float (via fround(f()))"
" or coerced to double (via +f())";
JS_ASSERT(expr->isKind(PNK_CALL));
return CheckFRoundArg(f, expr, def, type, callError);
}
static bool
CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
{
@ -4358,7 +4665,7 @@ CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
switch (expr->getKind()) {
case PNK_NAME: return CheckVarRef(f, expr, def, type);
case PNK_ELEM: return CheckArrayLoad(f, expr, def, type);
case PNK_ELEM: return CheckLoadArray(f, expr, def, type);
case PNK_ASSIGN: return CheckAssign(f, expr, def, type);
case PNK_POS: return CheckPos(f, expr, def, type);
case PNK_NOT: return CheckNot(f, expr, def, type);
@ -4367,10 +4674,7 @@ CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
case PNK_COMMA: return CheckComma(f, expr, def, type);
case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type);
case PNK_STAR: return CheckMultiply(f, expr, def, type);
case PNK_CALL: return f.fail(expr, "all function calls must either be ignored (via "
"f(); or comma-expression), coerced to signed "
"(via f()|0) or coerced to double (via +f())");
case PNK_CALL: return CheckUncoercedCall(f, expr, def, type);
case PNK_ADD:
case PNK_SUB: return CheckAddOrSub(f, expr, def, type);
@ -4792,6 +5096,8 @@ CheckReturn(FunctionCompiler &f, ParseNode *returnStmt)
retType = RetType::Signed;
else if (type.isDouble())
retType = RetType::Double;
else if (type.isFloat())
retType = RetType::Float;
else if (type.isVoid())
retType = RetType::Void;
else
@ -5605,7 +5911,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
masm.load32(src, scratch);
masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
} else {
JS_ASSERT(iter.mirType() == MIRType_Double);
JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32);
masm.loadDouble(src, ScratchFloatReg);
masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase()));
}
@ -5629,6 +5935,9 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
case RetType::Signed:
masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
break;
case RetType::Float:
masm.convertFloatToDouble(ReturnFloatReg, ReturnFloatReg);
// Fall through as ReturnFloatReg now contains a Double
case RetType::Double:
masm.canonicalizeDouble(ReturnFloatReg);
masm.storeDouble(ReturnFloatReg, Address(argv, 0));
@ -5873,6 +6182,9 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnFloatReg);
break;
case RetType::Float:
MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
break;
}
// Note: the caller is IonMonkey code which means there are no non-volatile
@ -5924,6 +6236,9 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnFloatReg);
break;
case RetType::Float:
MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
break;
}
masm.freeStack(reserveSize + sizeof(int32_t));
@ -6128,6 +6443,9 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
case RetType::Double:
masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
break;
case RetType::Float:
MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
break;
}
masm.bind(&done);

View File

@ -70,10 +70,17 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl
switch (global.varInitKind()) {
case AsmJSModule::Global::InitConstant: {
const Value &v = global.varInitConstant();
if (v.isInt32())
switch (global.varInitCoercion()) {
case AsmJS_ToInt32:
*(int32_t *)datum = v.toInt32();
else
break;
case AsmJS_ToNumber:
*(double *)datum = v.toDouble();
break;
case AsmJS_FRound:
*(float *)datum = static_cast<float>(v.toDouble());
break;
}
break;
}
case AsmJSModule::Global::InitImport: {
@ -82,7 +89,7 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl
if (!GetDataProperty(cx, importVal, field, &v))
return false;
switch (global.varImportCoercion()) {
switch (global.varInitCoercion()) {
case AsmJS_ToInt32:
if (!ToInt32(cx, v, (int32_t *)datum))
return false;
@ -91,6 +98,10 @@ ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Gl
if (!ToNumber(cx, v, (double *)datum))
return false;
break;
case AsmJS_FRound:
if (!RoundFloat32(cx, v, (float *)datum))
return false;
break;
}
break;
}
@ -157,6 +168,7 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob
case AsmJSMathBuiltin_abs: native = js_math_abs; break;
case AsmJSMathBuiltin_atan2: native = math_atan2; break;
case AsmJSMathBuiltin_imul: native = math_imul; break;
case AsmJSMathBuiltin_fround: native = math_fround; break;
}
if (!IsNativeFunction(v, native))
@ -364,6 +376,10 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
return false;
break;
case AsmJS_FRound:
if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
return false;
break;
}
}

View File

@ -235,28 +235,50 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
return FuncCast(NumberMod);
case AsmJSImm_SinD:
return FuncCast<double (double)>(sin);
case AsmJSImm_SinF:
return FuncCast<float (float)>(sinf);
case AsmJSImm_CosD:
return FuncCast<double (double)>(cos);
case AsmJSImm_CosF:
return FuncCast<float (float)>(cosf);
case AsmJSImm_TanD:
return FuncCast<double (double)>(tan);
case AsmJSImm_TanF:
return FuncCast<float (float)>(tanf);
case AsmJSImm_ASinD:
return FuncCast<double (double)>(asin);
case AsmJSImm_ASinF:
return FuncCast<float (float)>(asinf);
case AsmJSImm_ACosD:
return FuncCast<double (double)>(acos);
case AsmJSImm_ACosF:
return FuncCast<float (float)>(acosf);
case AsmJSImm_ATanD:
return FuncCast<double (double)>(atan);
case AsmJSImm_ATanF:
return FuncCast<float (float)>(atanf);
case AsmJSImm_CeilD:
return FuncCast<double (double)>(ceil);
case AsmJSImm_CeilF:
return FuncCast<float (float)>(ceilf);
case AsmJSImm_FloorD:
return FuncCast<double (double)>(floor);
case AsmJSImm_FloorF:
return FuncCast<float (float)>(floorf);
case AsmJSImm_ExpD:
return FuncCast<double (double)>(exp);
case AsmJSImm_ExpF:
return FuncCast<float (float)>(expf);
case AsmJSImm_LogD:
return FuncCast<double (double)>(log);
case AsmJSImm_LogF:
return FuncCast<float (float)>(logf);
case AsmJSImm_PowD:
return FuncCast(ecmaPow);
case AsmJSImm_ATan2D:
return FuncCast(ecmaAtan2);
case AsmJSImm_Invalid:
break;
}
MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind");

View File

@ -29,7 +29,8 @@ namespace js {
enum AsmJSCoercion
{
AsmJS_ToInt32,
AsmJS_ToNumber
AsmJS_ToNumber,
AsmJS_FRound
};
// The asm.js spec recognizes this set of builtin Math functions.
@ -39,7 +40,8 @@ enum AsmJSMathBuiltin
AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul
AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
AsmJSMathBuiltin_fround
};
// Static-link data is used to patch a module either after it has been
@ -105,9 +107,9 @@ class AsmJSModule
struct {
uint32_t index_;
VarInitKind initKind_;
AsmJSCoercion coercion_;
union {
Value constant_; // will only contain int32/double
AsmJSCoercion coercion_;
} init;
} var;
uint32_t ffiIndex_;
@ -151,10 +153,9 @@ class AsmJSModule
JS_ASSERT(pod.u.var.initKind_ == InitConstant);
return pod.u.var.init.constant_;
}
AsmJSCoercion varImportCoercion() const {
AsmJSCoercion varInitCoercion() const {
JS_ASSERT(pod.which_ == Variable);
JS_ASSERT(pod.u.var.initKind_ == InitImport);
return pod.u.var.init.coercion_;
return pod.u.var.coercion_;
}
PropertyName *varImportField() const {
JS_ASSERT(pod.which_ == Variable);
@ -455,13 +456,14 @@ class AsmJSModule
return charsBegin_ + pod.charsLength_;
}
bool addGlobalVarInitConstant(const Value &v, uint32_t *globalIndex) {
bool addGlobalVarInitConstant(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) {
JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0);
if (pod.numGlobalVars_ == UINT32_MAX)
return false;
Global g(Global::Variable, nullptr);
g.pod.u.var.initKind_ = Global::InitConstant;
g.pod.u.var.init.constant_ = v;
g.pod.u.var.coercion_ = coercion;
g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
return globals_.append(g);
}
@ -469,7 +471,7 @@ class AsmJSModule
JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0);
Global g(Global::Variable, name);
g.pod.u.var.initKind_ = Global::InitImport;
g.pod.u.var.init.coercion_ = coercion;
g.pod.u.var.coercion_ = coercion;
g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
return globals_.append(g);
}

View File

@ -3300,7 +3300,7 @@ LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins)
if (abi.argInRegister())
return defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg()));
JS_ASSERT(ins->type() == MIRType_Int32 || ins->type() == MIRType_Double);
JS_ASSERT(IsNumberType(ins->type()));
LAllocation::Kind argKind = ins->type() == MIRType_Int32
? LAllocation::INT_ARGUMENT
: LAllocation::DOUBLE_ARGUMENT;
@ -3312,7 +3312,7 @@ LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins)
{
MDefinition *rval = ins->getOperand(0);
LAsmJSReturn *lir = new(alloc()) LAsmJSReturn;
if (rval->type() == MIRType_Double)
if (IsFloatingPointType(rval->type()))
lir->setOperand(0, useFixed(rval, ReturnFloatReg));
else if (rval->type() == MIRType_Int32)
lir->setOperand(0, useFixed(rval, ReturnReg));
@ -3330,7 +3330,7 @@ LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins)
bool
LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins)
{
if (ins->arg()->type() == MIRType_Double) {
if (IsFloatingPointType(ins->arg()->type())) {
JS_ASSERT(!ins->arg()->isEmittedAtUses());
return add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
}

View File

@ -9241,7 +9241,9 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
: MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
{
setMovable();
if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
if (vt == ArrayBufferView::TYPE_FLOAT32)
setResultType(MIRType_Float32);
else if (vt == ArrayBufferView::TYPE_FLOAT64)
setResultType(MIRType_Double);
else
setResultType(MIRType_Int32);
@ -9291,7 +9293,7 @@ class MAsmJSLoadGlobalVar : public MNullaryInstruction
MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant)
: globalDataOffset_(globalDataOffset), isConstant_(isConstant)
{
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
JS_ASSERT(IsNumberType(type));
setResultType(type);
setMovable();
}

View File

@ -45,6 +45,7 @@ ABIArgGenerator::next(MIRType type)
current_ = ABIArg(Register::FromCode(intRegIndex_));
intRegIndex_++;
break;
case MIRType_Float32:
case MIRType_Double:
if (floatRegIndex_ == NumFloatArgRegs) {
static const int align = sizeof(double) - 1;

View File

@ -1969,12 +1969,10 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
JS_ASSERT(ptrImm >= 0);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->output()));
if (size == 32) {
if (size == 32)
masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
} else {
else
masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
ToRegister(ins->output()), Offset, Assembler::Always);
@ -1987,12 +1985,10 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
if (mir->skipBoundsCheck()) {
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->output()));
if (size == 32) {
if (size == 32)
masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
} else {
else
masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
ToRegister(ins->output()), Offset, Assembler::Always);
@ -2003,12 +1999,12 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
if (isFloat) {
FloatRegister dst = ToFloatRegister(ins->output());
masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual);
VFPRegister vd(dst);
if (size == 32) {
masm.convertDoubleToFloat(NANReg, dst, Assembler::AboveOrEqual);
masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Below);
} else {
masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual);
masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below);
}
} else {
@ -2044,12 +2040,10 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
JS_ASSERT(ptrImm >= 0);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32) {
masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always);
masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
} else {
if (size == 32)
masm.ma_vstr(vd.singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
else
masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
ToRegister(ins->value()), Offset, Assembler::Always);
@ -2064,7 +2058,7 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32)
masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always);
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
else
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
} else {
@ -2078,7 +2072,7 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32)
masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Below);
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
else
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below);
} else {
@ -2229,10 +2223,14 @@ CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
{
const MAsmJSLoadGlobalVar *mir = ins->mir();
unsigned addr = mir->globalDataOffset();
if (mir->type() == MIRType_Int32)
if (mir->type() == MIRType_Int32) {
masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()));
else
} else if (mir->type() == MIRType_Float32) {
VFPRegister vd(ToFloatRegister(ins->output()));
masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay());
} else {
masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output()));
}
return true;
}
@ -2242,12 +2240,16 @@ CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
const MAsmJSStoreGlobalVar *mir = ins->mir();
MIRType type = mir->value()->type();
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
JS_ASSERT(IsNumberType(type));
unsigned addr = mir->globalDataOffset();
if (mir->value()->type() == MIRType_Int32)
if (mir->value()->type() == MIRType_Int32) {
masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
else
} else if (mir->value()->type() == MIRType_Float32) {
VFPRegister vd(ToFloatRegister(ins->value()));
masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr));
} else {
masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr));
}
return true;
}

View File

@ -176,10 +176,19 @@ class CodeGeneratorARM : public CodeGeneratorShared
bool generateInvalidateEpilogue();
protected:
void postAsmJSCall(LAsmJSCall *lir) {
#if !defined(JS_CPU_ARM_HARDFP)
#ifndef JS_CPU_ARM_HARDFP
if (lir->mir()->callee().which() == MAsmJSCall::Callee::Builtin) {
if (lir->mir()->type() == MIRType_Double)
switch (lir->mir()->type()) {
case MIRType_Double:
masm.ma_vxfer(r0, r1, d0);
break;
case MIRType_Float32:
masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(),
Assembler::CoreToFloat);
break;
default:
break;
}
}
#endif
}

View File

@ -412,6 +412,9 @@ LIRGeneratorARM::visitAsmJSNeg(MAsmJSNeg *ins)
if (ins->type() == MIRType_Int32)
return define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
if(ins->type() == MIRType_Float32)
return define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
JS_ASSERT(ins->type() == MIRType_Double);
return define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
}

View File

@ -74,9 +74,10 @@ MacroAssemblerARM::convertUInt32ToFloat32(const Register &src, const FloatRegist
as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay());
}
void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest)
void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest,
Condition c)
{
as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src));
as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c);
}
// there are two options for implementing emitTruncateDouble.

View File

@ -50,7 +50,8 @@ class MacroAssemblerARM : public Assembler
void convertInt32ToDouble(const Address &src, FloatRegister dest);
void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest);
void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest,
Condition c = Always);
void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail,
bool negativeZeroCheck = true);
@ -1494,11 +1495,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
return as_cmp(bounded, Imm8(0));
}
void storeFloat(VFPRegister src, Register base, Register index, Condition cond) {
as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), src, false, cond);
ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), base, index, 0, cond);
}
void moveFloat(FloatRegister src, FloatRegister dest) {
as_vmov(VFPRegister(src).singleOverlay(), VFPRegister(dest).singleOverlay());
}

View File

@ -691,17 +691,28 @@ enum AsmJSImmKind
#endif
AsmJSImm_ModD,
AsmJSImm_SinD,
AsmJSImm_SinF,
AsmJSImm_CosD,
AsmJSImm_CosF,
AsmJSImm_TanD,
AsmJSImm_TanF,
AsmJSImm_ASinD,
AsmJSImm_ASinF,
AsmJSImm_ACosD,
AsmJSImm_ACosF,
AsmJSImm_ATanD,
AsmJSImm_ATanF,
AsmJSImm_CeilD,
AsmJSImm_CeilF,
AsmJSImm_FloorD,
AsmJSImm_FloorF,
AsmJSImm_ExpD,
AsmJSImm_ExpF,
AsmJSImm_LogD,
AsmJSImm_LogF,
AsmJSImm_PowD,
AsmJSImm_ATan2D
AsmJSImm_ATan2D,
AsmJSImm_Invalid
};
// Pointer to be embedded as an immediate in asm.js code.

View File

@ -227,6 +227,9 @@ LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins)
if (ins->type() == MIRType_Int32)
return defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins, 0);
if (ins->type() == MIRType_Float32)
return defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins, 0);
JS_ASSERT(ins->type() == MIRType_Double);
return defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins, 0);
}

View File

@ -39,6 +39,7 @@ ABIArgGenerator::next(MIRType type)
case MIRType_Pointer:
current_ = ABIArg(IntArgRegs[regIndex_++]);
break;
case MIRType_Float32:
case MIRType_Double:
current_ = ABIArg(FloatArgRegs[regIndex_++]);
break;
@ -58,6 +59,7 @@ ABIArgGenerator::next(MIRType type)
current_ = ABIArg(IntArgRegs[intRegIndex_++]);
break;
case MIRType_Double:
case MIRType_Float32:
if (floatRegIndex_ == NumFloatArgRegs) {
current_ = ABIArg(stackOffset_);
stackOffset_ += sizeof(uint64_t);

View File

@ -409,15 +409,6 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
}
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(ins->output());
uint32_t before = masm.size();
masm.loadFloat(srcAddr, dest);
uint32_t after = masm.size();
masm.cvtss2sd(dest, dest);
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
}
uint32_t before = masm.size();
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movsbl(srcAddr, ToRegister(ins->output())); break;
@ -426,6 +417,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
case ArrayBufferView::TYPE_UINT16: masm.movzwl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_FLOAT32: masm.loadFloat(srcAddr, ToFloatRegister(ins->output())); break;
case ArrayBufferView::TYPE_FLOAT64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
@ -453,14 +445,6 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
}
if (vt == ArrayBufferView::TYPE_FLOAT32) {
masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg);
uint32_t before = masm.size();
masm.storeFloat(ScratchFloatReg, dstAddr);
uint32_t after = masm.size();
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
uint32_t before = masm.size();
if (ins->value()->isConstant()) {
switch (vt) {
@ -480,6 +464,7 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
case ArrayBufferView::TYPE_UINT16: masm.movw(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movl(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT32: masm.storeFloat(ToFloatRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
@ -508,7 +493,7 @@ CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
MAsmJSStoreGlobalVar *mir = ins->mir();
MIRType type = mir->value()->type();
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
JS_ASSERT(IsNumberType(type));
CodeOffsetLabel label;
if (type == MIRType_Int32)

View File

@ -278,6 +278,16 @@ class Assembler : public AssemblerX86Shared
}
}
void fstp32(const Operand &src) {
switch (src.kind()) {
case Operand::MEM_REG_DISP:
masm.fstp32_m(src.disp(), src.base());
break;
default:
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
}
}
void cmpl(const Register src, ImmWord ptr) {
masm.cmpl_ir(ptr.value, src.code());
}

View File

@ -410,16 +410,21 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
{
AnyRegister dest_;
bool isFloat32Load_;
public:
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest) : dest_(dest) {}
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
: dest_(dest), isFloat32Load_(isFloat32Load)
{}
const AnyRegister &dest() const { return dest_; }
bool isFloat32Load() const { return isFloat32Load_; }
bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
};
template<typename T>
void
CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
@ -429,6 +434,7 @@ CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, co
case ArrayBufferView::TYPE_UINT16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
@ -436,19 +442,11 @@ CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, co
template<typename T>
bool
CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
CodeGeneratorX86::loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(out);
uint32_t before = masm.size();
masm.movssWithPatch(srcAddr, dest);
uint32_t after = masm.size();
masm.cvtss2sd(dest, dest);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest)));
}
uint32_t before = masm.size();
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
loadViewTypeElement(vt, srcAddr, out);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out)));
}
@ -458,13 +456,15 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
{
const MLoadTypedArrayElementStatic *mir = ins->mir();
ArrayBufferView::ViewType vt = mir->viewType();
JS_ASSERT_IF(vt == ArrayBufferView::TYPE_FLOAT32, mir->type() == MIRType_Float32);
Register ptr = ToRegister(ins->ptr());
const LDefinition *out = ins->output();
OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
bool isFloat32Load = (vt == ArrayBufferView::TYPE_FLOAT32);
if (!mir->fallible()) {
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
if (!addOutOfLineCode(ool))
return false;
}
@ -476,18 +476,11 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
return false;
Address srcAddr(ptr, (int32_t) mir->base());
if (vt == ArrayBufferView::TYPE_FLOAT32) {
JS_ASSERT(mir->type() == MIRType_Float32);
FloatRegister dest = ToFloatRegister(out);
masm.movssWithPatch(srcAddr, dest);
masm.canonicalizeFloat(dest);
if (ool)
masm.bind(ool->rejoin());
return true;
}
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
loadViewTypeElement(vt, srcAddr, out);
if (vt == ArrayBufferView::TYPE_FLOAT64)
masm.canonicalizeDouble(ToFloatRegister(out));
if (vt == ArrayBufferView::TYPE_FLOAT32)
masm.canonicalizeFloat(ToFloatRegister(out));
if (ool)
masm.bind(ool->rejoin());
return true;
@ -507,33 +500,25 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
// immediate in the instruction. This displacement will fixed up when the
// base address is known during dynamic linking (AsmJSModule::initHeap).
PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32());
return loadViewTypeElement(vt, srcAddr, out);
return loadAndNoteViewTypeElement(vt, srcAddr, out);
}
Register ptrReg = ToRegister(ptr);
Address srcAddr(ptrReg, 0);
if (mir->skipBoundsCheck())
return loadViewTypeElement(vt, srcAddr, out);
return loadAndNoteViewTypeElement(vt, srcAddr, out);
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
bool isFloat32Load = vt == ArrayBufferView::TYPE_FLOAT32;
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
if (!addOutOfLineCode(ool))
return false;
CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
masm.j(Assembler::AboveOrEqual, ool->entry());
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(out);
uint32_t before = masm.size();
masm.movssWithPatch(srcAddr, dest);
uint32_t after = masm.size();
masm.cvtss2sd(dest, dest);
masm.bind(ool->rejoin());
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest), cmp.offset()));
}
uint32_t before = masm.size();
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
loadViewTypeElement(vt, srcAddr, out);
uint32_t after = masm.size();
masm.bind(ool->rejoin());
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
@ -543,7 +528,10 @@ bool
CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
{
if (ool->dest().isFloat()) {
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
if (ool->isFloat32Load())
masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
else
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
} else {
Register destReg = ool->dest().gpr();
masm.mov(ImmWord(0), destReg);
@ -554,8 +542,8 @@ CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArra
template<typename T>
void
CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
{
switch (vt) {
case ArrayBufferView::TYPE_INT8:
@ -565,6 +553,7 @@ CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, c
case ArrayBufferView::TYPE_UINT16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
@ -572,18 +561,11 @@ CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, c
template<typename T>
bool
CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
CodeGeneratorX86::storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
{
if (vt == ArrayBufferView::TYPE_FLOAT32) {
uint32_t before = masm.size();
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
masm.movssWithPatch(ScratchFloatReg, dstAddr);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
uint32_t before = masm.size();
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
storeViewTypeElement(vt, value, dstAddr);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
@ -602,13 +584,7 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati
masm.j(Assembler::AboveOrEqual, &rejoin);
Address dstAddr(ptr, (int32_t) mir->base());
if (vt == ArrayBufferView::TYPE_FLOAT32) {
JS_ASSERT(mir->value()->type() == MIRType_Float32);
masm.movssWithPatch(ToFloatRegister(value), dstAddr);
masm.bind(&rejoin);
return true;
}
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
storeViewTypeElement(vt, value, dstAddr);
masm.bind(&rejoin);
return true;
}
@ -627,29 +603,21 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
// immediate in the instruction. This displacement will fixed up when the
// base address is known during dynamic linking (AsmJSModule::initHeap).
PatchedAbsoluteAddress dstAddr((void *) ptr->toConstant()->toInt32());
return storeViewTypeElement(vt, value, dstAddr);
return storeAndNoteViewTypeElement(vt, value, dstAddr);
}
Register ptrReg = ToRegister(ptr);
Address dstAddr(ptrReg, 0);
if (mir->skipBoundsCheck())
return storeViewTypeElement(vt, value, dstAddr);
return storeAndNoteViewTypeElement(vt, value, dstAddr);
CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
Label rejoin;
masm.j(Assembler::AboveOrEqual, &rejoin);
if (vt == ArrayBufferView::TYPE_FLOAT32) {
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
uint32_t before = masm.size();
masm.movssWithPatch(ScratchFloatReg, dstAddr);
uint32_t after = masm.size();
masm.bind(&rejoin);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
}
uint32_t before = masm.size();
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
storeViewTypeElement(vt, value, dstAddr);
uint32_t after = masm.size();
masm.bind(&rejoin);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
@ -659,10 +627,14 @@ bool
CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
{
MAsmJSLoadGlobalVar *mir = ins->mir();
MIRType type = mir->type();
JS_ASSERT(IsNumberType(type));
CodeOffsetLabel label;
if (mir->type() == MIRType_Int32)
if (type == MIRType_Int32)
label = masm.movlWithPatch(PatchedAbsoluteAddress(), ToRegister(ins->output()));
else if (type == MIRType_Float32)
label = masm.movssWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
else
label = masm.movsdWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
@ -675,11 +647,13 @@ CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
MAsmJSStoreGlobalVar *mir = ins->mir();
MIRType type = mir->value()->type();
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
JS_ASSERT(IsNumberType(type));
CodeOffsetLabel label;
if (type == MIRType_Int32)
label = masm.movlWithPatch(ToRegister(ins->value()), PatchedAbsoluteAddress());
else if (type == MIRType_Float32)
label = masm.movssWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
else
label = masm.movsdWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
@ -713,13 +687,18 @@ void
CodeGeneratorX86::postAsmJSCall(LAsmJSCall *lir)
{
MAsmJSCall *mir = lir->mir();
if (mir->type() != MIRType_Double || mir->callee().which() != MAsmJSCall::Callee::Builtin)
if (!IsFloatingPointType(mir->type()) || mir->callee().which() != MAsmJSCall::Callee::Builtin)
return;
masm.reserveStack(sizeof(double));
masm.fstp(Operand(esp, 0));
masm.loadDouble(Operand(esp, 0), ReturnFloatReg);
masm.freeStack(sizeof(double));
if (mir->type() == MIRType_Float32) {
Operand op(esp, -sizeof(float));
masm.fstp32(op);
masm.loadFloat(op, ReturnFloatReg);
} else {
Operand op(esp, -sizeof(double));
masm.fstp(op);
masm.loadDouble(op, ReturnFloatReg);
}
}
void
@ -1013,7 +992,12 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool)
masm.setupUnalignedABICall(1, output);
masm.cvtss2sd(input, input);
masm.passABIArg(input);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
if (gen->compilingAsmJS())
masm.callWithABI(AsmJSImm_ToInt32);
else
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
masm.storeCallResult(output);
masm.pop(input);

View File

@ -30,16 +30,16 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
ValueOperand ToTempValue(LInstruction *ins, size_t pos);
template<typename T>
bool loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
bool loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
void loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
void loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
bool storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
bool storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr);
template<typename T>
void storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr);
void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
const Register &elements, const LAllocation *index);

View File

@ -460,6 +460,16 @@ js::math_imul(JSContext *cx, unsigned argc, Value *vp)
return true;
}
// Implements Math.fround (20.2.2.16) up to step 3
bool
js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
{
double d;
bool success = ToNumber(cx, v, &d);
*out = static_cast<float>(d);
return success;
}
bool
js::math_fround(JSContext *cx, unsigned argc, Value *vp)
{
@ -470,11 +480,10 @@ js::math_fround(JSContext *cx, unsigned argc, Value *vp)
return true;
}
double x;
if (!ToNumber(cx, args[0], &x))
float f;
if (!RoundFloat32(cx, args[0], &f))
return false;
float f = x;
args.rval().setDouble(static_cast<double>(f));
return true;
}

View File

@ -84,6 +84,9 @@ namespace js {
extern bool
math_imul(JSContext *cx, unsigned argc, js::Value *vp);
extern bool
RoundFloat32(JSContext *cx, Handle<Value> v, float *out);
extern bool
math_fround(JSContext *cx, unsigned argc, js::Value *vp);