--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -490,17 +490,17 @@ class RetType
Double = Type::Double,
Float = Type::Float
};
private:
Which which_;
public:
- RetType() {}
+ RetType() : which_(Which(-1)) {}
RetType(Which w) : which_(w) {}
RetType(AsmJSCoercion coercion) {
switch (coercion) {
case AsmJS_ToInt32: which_ = Signed; break;
case AsmJS_ToNumber: which_ = Double; break;
case AsmJS_FRound: which_ = Float; break;
}
}
@@ -587,35 +587,34 @@ class VarType
}
MIRType toMIRType() const {
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 {
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 || 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;
+ default:;
}
+ MOZ_ASSUME_UNREACHABLE("FromMIRType MIR type not handled");
}
static VarType FromCheckedType(Type type) {
JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
if (type.isMaybeDouble())
return Double;
else if (type.isFloatish())
return Float;
else
@@ -724,197 +723,16 @@ bool operator==(const Signature &lhs, co
static inline
bool operator!=(const Signature &lhs, const Signature &rhs)
{
return !(lhs == rhs);
}
/*****************************************************************************/
-// Numeric literal utilities
-
-namespace {
-
-// Represents the type and value of an asm.js numeric literal.
-//
-// A literal is a double iff the literal contains an exponent or decimal point
-// (even if the fractional part is 0). Otherwise, integers may be classified:
-// fixnum: [0, 2^31)
-// negative int: [-2^31, 0)
-// big unsigned: [2^31, 2^32)
-// out of range: otherwise
-class NumLit
-{
- public:
- enum Which {
- Fixnum = Type::Fixnum,
- NegativeInt = Type::Signed,
- BigUnsigned = Type::Unsigned,
- Double = Type::Double,
- OutOfRangeInt = -1
- };
-
- private:
- Which which_;
- Value v_;
-
- public:
- NumLit() {}
-
- NumLit(Which w, Value v)
- : which_(w), v_(v)
- {}
-
- Which which() const {
- return which_;
- }
-
- int32_t toInt32() const {
- JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
- return v_.toInt32();
- }
-
- double toDouble() const {
- return v_.toDouble();
- }
-
- Type type() const {
- JS_ASSERT(which_ != OutOfRangeInt);
- return Type::Which(which_);
- }
-
- Value value() const {
- JS_ASSERT(which_ != OutOfRangeInt);
- return v_;
- }
-};
-
-} /* anonymous namespace */
-
-// Note: '-' is never rolled into the number; numbers are always positive and
-// negations must be applied manually.
-static bool
-IsNumericLiteral(ParseNode *pn)
-{
- return pn->isKind(PNK_NUMBER) ||
- (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
-}
-
-static NumLit
-ExtractNumericLiteral(ParseNode *pn)
-{
- // The JS grammar treats -42 as -(42) (i.e., with separate grammar
- // productions) for the unary - and literal 42). However, the asm.js spec
- // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
- // so fold the two potential parse nodes into a single double value.
- JS_ASSERT(IsNumericLiteral(pn));
- ParseNode *numberNode;
- double d;
- if (pn->isKind(PNK_NEG)) {
- numberNode = UnaryKid(pn);
- d = -NumberNodeValue(numberNode);
- } else {
- numberNode = pn;
- d = NumberNodeValue(numberNode);
- }
-
- // The asm.js spec syntactically distinguishes any literal containing a
- // decimal point or the literal -0 as having double type.
- if (NumberNodeHasFrac(numberNode) || IsNegativeZero(d))
- return NumLit(NumLit::Double, DoubleValue(d));
-
- // The syntactic checks above rule out these double values.
- JS_ASSERT(!IsNegativeZero(d));
- JS_ASSERT(!IsNaN(d));
-
- // Although doubles can only *precisely* represent 53-bit integers, they
- // can *imprecisely* represent integers much bigger than an int64_t.
- // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
- // is undefined, so test against the integer bounds using doubles.
- if (d < double(INT32_MIN) || d > double(UINT32_MAX))
- return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
-
- // With the above syntactic and range limitations, d is definitely an
- // integer in the range [INT32_MIN, UINT32_MAX] range.
- int64_t i64 = int64_t(d);
- if (i64 >= 0) {
- if (i64 <= INT32_MAX)
- return NumLit(NumLit::Fixnum, Int32Value(i64));
- JS_ASSERT(i64 <= UINT32_MAX);
- return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
- }
- JS_ASSERT(i64 >= INT32_MIN);
- 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)
-{
- if (!IsNumericLiteral(pn))
- return false;
-
- NumLit literal = ExtractNumericLiteral(pn);
- switch (literal.which()) {
- case NumLit::Fixnum:
- case NumLit::BigUnsigned:
- case NumLit::NegativeInt:
- *u32 = uint32_t(literal.toInt32());
- return true;
- case NumLit::Double:
- case NumLit::OutOfRangeInt:
- return false;
- }
-
- MOZ_ASSUME_UNREACHABLE("Bad literal type");
-}
-
-static inline bool
-IsBits32(ParseNode *pn, int32_t i)
-{
- if (!IsNumericLiteral(pn))
- return false;
-
- NumLit literal = ExtractNumericLiteral(pn);
- switch (literal.which()) {
- case NumLit::Fixnum:
- case NumLit::BigUnsigned:
- case NumLit::NegativeInt:
- return literal.toInt32() == i;
- case NumLit::Double:
- case NumLit::OutOfRangeInt:
- return false;
- }
-
- MOZ_ASSUME_UNREACHABLE("Bad literal type");
-}
-
-/*****************************************************************************/
// Typed array utilities
static Type
TypedArrayLoadType(ArrayBufferView::ViewType viewType)
{
switch (viewType) {
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_INT16:
@@ -1842,16 +1660,242 @@ class MOZ_STACK_CLASS ModuleCompiler
*module = module_.forget();
return true;
}
};
} /* anonymous namespace */
/*****************************************************************************/
+// Numeric literal utilities
+
+namespace {
+
+// Represents the type and value of an asm.js numeric literal.
+//
+// A literal is a double iff the literal contains an exponent or decimal point
+// (even if the fractional part is 0). Otherwise, integers may be classified:
+// fixnum: [0, 2^31)
+// negative int: [-2^31, 0)
+// big unsigned: [2^31, 2^32)
+// out of range: otherwise
+// Lastly, a literal may be a float literal which is any double or integer
+// literal coerced with Math.fround.
+class NumLit
+{
+ public:
+ enum Which {
+ Fixnum = Type::Fixnum,
+ NegativeInt = Type::Signed,
+ BigUnsigned = Type::Unsigned,
+ Double = Type::Double,
+ Float = Type::Float,
+ OutOfRangeInt = -1
+ };
+
+ private:
+ Which which_;
+ Value v_;
+
+ public:
+ NumLit() {}
+
+ NumLit(Which w, Value v)
+ : which_(w), v_(v)
+ {}
+
+ Which which() const {
+ return which_;
+ }
+
+ int32_t toInt32() const {
+ JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
+ return v_.toInt32();
+ }
+
+ double toDouble() const {
+ JS_ASSERT(which_ == Double);
+ return v_.toDouble();
+ }
+
+ float toFloat() const {
+ JS_ASSERT(which_ == Float);
+ return float(v_.toDouble());
+ }
+
+ Value value() const {
+ JS_ASSERT(which_ != OutOfRangeInt);
+ return v_;
+ }
+
+ bool hasType() const {
+ return which_ != OutOfRangeInt;
+ }
+
+ Type type() const {
+ JS_ASSERT(hasType());
+ return Type::Which(which_);
+ }
+
+ VarType varType() const {
+ JS_ASSERT(hasType());
+ switch (which_) {
+ case NumLit::Fixnum:
+ case NumLit::NegativeInt:
+ case NumLit::BigUnsigned:
+ return VarType::Int;
+ case NumLit::Double:
+ return VarType::Double;
+ case NumLit::Float:
+ return VarType::Float;
+ case NumLit::OutOfRangeInt:;
+ }
+ MOZ_ASSUME_UNREACHABLE("Unexpected NumLit type");
+ }
+};
+
+} /* anonymous namespace */
+
+static bool
+IsNumericNonFloatLiteral(ParseNode *pn)
+{
+ // Note: '-' is never rolled into the number; numbers are always positive
+ // and negations must be applied manually.
+ return pn->isKind(PNK_NUMBER) ||
+ (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
+}
+
+static bool
+IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr)
+{
+ if (!pn->isKind(PNK_CALL))
+ return false;
+
+ ParseNode *callee = CallCallee(pn);
+ if (!callee->isKind(PNK_NAME))
+ return false;
+
+ const ModuleCompiler::Global *global = m.lookupGlobal(callee->name());
+ if (!global ||
+ global->which() != ModuleCompiler::Global::MathBuiltin ||
+ global->mathBuiltin() != AsmJSMathBuiltin_fround)
+ {
+ return false;
+ }
+
+ if (CallArgListLength(pn) != 1)
+ return false;
+
+ if (coercedExpr)
+ *coercedExpr = CallArgList(pn);
+
+ return true;
+}
+
+static bool
+IsNumericFloatLiteral(ModuleCompiler &m, ParseNode *pn)
+{
+ ParseNode *coercedExpr;
+ if (!IsFloatCoercion(m, pn, &coercedExpr))
+ return false;
+
+ return IsNumericNonFloatLiteral(coercedExpr);
+}
+
+static bool
+IsNumericLiteral(ModuleCompiler &m, ParseNode *pn)
+{
+ return IsNumericNonFloatLiteral(pn) ||
+ IsNumericFloatLiteral(m, pn);
+}
+
+// The JS grammar treats -42 as -(42) (i.e., with separate grammar
+// productions) for the unary - and literal 42). However, the asm.js spec
+// recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
+// so fold the two potential parse nodes into a single double value.
+static double
+ExtractNumericNonFloatValue(ParseNode **pn)
+{
+ JS_ASSERT(IsNumericNonFloatLiteral(*pn));
+
+ if ((*pn)->isKind(PNK_NEG)) {
+ *pn = UnaryKid(*pn);
+ return -NumberNodeValue(*pn);
+ }
+
+ return NumberNodeValue(*pn);
+}
+
+static NumLit
+ExtractNumericLiteral(ModuleCompiler &m, ParseNode *pn)
+{
+ JS_ASSERT(IsNumericLiteral(m, pn));
+
+ // Float literals are explicitly coerced and thus the coerced literal may be
+ // any valid (non-float) numeric literal.
+ if (pn->isKind(PNK_CALL)) {
+ pn = CallArgList(pn);
+ double d = ExtractNumericNonFloatValue(&pn);
+ return NumLit(NumLit::Float, DoubleValue(d));
+ }
+
+ double d = ExtractNumericNonFloatValue(&pn);
+
+ // The asm.js spec syntactically distinguishes any literal containing a
+ // decimal point or the literal -0 as having double type.
+ if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
+ return NumLit(NumLit::Double, DoubleValue(d));
+
+ // The syntactic checks above rule out these double values.
+ JS_ASSERT(!IsNegativeZero(d));
+ JS_ASSERT(!IsNaN(d));
+
+ // Although doubles can only *precisely* represent 53-bit integers, they
+ // can *imprecisely* represent integers much bigger than an int64_t.
+ // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
+ // is undefined, so test against the integer bounds using doubles.
+ if (d < double(INT32_MIN) || d > double(UINT32_MAX))
+ return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
+
+ // With the above syntactic and range limitations, d is definitely an
+ // integer in the range [INT32_MIN, UINT32_MAX] range.
+ int64_t i64 = int64_t(d);
+ if (i64 >= 0) {
+ if (i64 <= INT32_MAX)
+ return NumLit(NumLit::Fixnum, Int32Value(i64));
+ JS_ASSERT(i64 <= UINT32_MAX);
+ return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
+ }
+ JS_ASSERT(i64 >= INT32_MIN);
+ return NumLit(NumLit::NegativeInt, Int32Value(i64));
+}
+
+static inline bool
+IsLiteralInt(ModuleCompiler &m, ParseNode *pn, uint32_t *u32)
+{
+ if (!IsNumericLiteral(m, pn))
+ return false;
+
+ NumLit literal = ExtractNumericLiteral(m, pn);
+ switch (literal.which()) {
+ case NumLit::Fixnum:
+ case NumLit::BigUnsigned:
+ case NumLit::NegativeInt:
+ *u32 = uint32_t(literal.toInt32());
+ return true;
+ case NumLit::Double:
+ case NumLit::Float:
+ case NumLit::OutOfRangeInt:
+ return false;
+ }
+
+ MOZ_ASSUME_UNREACHABLE("Bad literal type");
+}
+
+/*****************************************************************************/
namespace {
// Encapsulates the compilation of a single function in an asm.js module. The
// function compiler handles the creation and final backend compilation of the
// MIR graph. Also see ModuleCompiler comment.
class FunctionCompiler
{
@@ -2060,32 +2104,21 @@ class FunctionCompiler
{
if (locals_.has(name))
return nullptr;
return m_.lookupGlobal(name);
}
/***************************** Code generation (after local scope setup) */
- MDefinition *constant(const Value &v)
+ MDefinition *constant(Value v, Type t)
{
if (!curBlock_)
return nullptr;
- JS_ASSERT(v.isNumber());
- MConstant *constant = MConstant::New(alloc(), v);
- curBlock_->add(constant);
- return constant;
- }
-
- MDefinition *constantFloat(float f)
- {
- if (!curBlock_)
- return nullptr;
-
- MConstant *constant = MConstant::NewAsmJS(alloc(), DoubleValue(double(f)), MIRType_Float32);
+ MConstant *constant = MConstant::NewAsmJS(alloc(), v, t.toMIRType());
curBlock_->add(constant);
return constant;
}
template <class T>
MDefinition *unary(MDefinition *op)
{
if (!curBlock_)
@@ -2921,96 +2954,54 @@ CheckPrecedingStatements(ModuleCompiler
return true;
}
static bool
CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
{
- NumLit literal = ExtractNumericLiteral(initNode);
- VarType type;
- switch (literal.which()) {
- case NumLit::Fixnum:
- case NumLit::NegativeInt:
- case NumLit::BigUnsigned:
- type = VarType::Int;
- break;
- case NumLit::Double:
- type = VarType::Double;
- break;
- case NumLit::OutOfRangeInt:
+ NumLit literal = ExtractNumericLiteral(m, initNode);
+ if (!literal.hasType())
return m.fail(initNode, "global initializer is out of representable integer range");
- }
- 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;
+
+ return m.addGlobalVarInitConstant(varName, literal.varType(), literal.value(), isConst);
}
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);
-
- if (!IsNumericLiteral(rhs))
+ uint32_t i;
+ if (!IsLiteralInt(m, rhs, &i) || i != 0)
return m.fail(rhs, "must use |0 for argument/return coercion");
-
- NumLit rhsLiteral = ExtractNumericLiteral(rhs);
- if (rhsLiteral.which() != NumLit::Fixnum || rhsLiteral.toInt32() != 0)
- return m.fail(rhs, "must use |0 for argument/return coercion");
-
*coercion = AsmJS_ToInt32;
if (coercedExpr)
*coercedExpr = BinaryLeft(coercionNode);
return true;
}
case PNK_POS: {
*coercion = AsmJS_ToNumber;
if (coercedExpr)
*coercedExpr = UnaryKid(coercionNode);
return true;
}
case PNK_CALL: {
*coercion = AsmJS_FRound;
- return CheckFloat32Coercion(m, coercionNode, coercedExpr, errorMessage);
+ if (!IsFloatCoercion(m, coercionNode, coercedExpr))
+ return m.fail(coercionNode, "call must be to fround coercion");
+ return true;
}
default:;
}
- return m.fail(coercionNode, errorMessage);
+ return m.fail(coercionNode, "must be of the form +x, fround(x) or x|0");
}
static bool
CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion,
ParseNode *coercedExpr, bool isConst)
{
if (!coercedExpr->isKind(PNK_DOT))
return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
@@ -3034,34 +3025,16 @@ CheckGlobalVariableInitImport(ModuleComp
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 = nullptr;
- 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)
{
ParseNode *ctorExpr = ListHead(newExpr);
if (!ctorExpr->isKind(PNK_DOT))
return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
ParseNode *base = DotBase(ctorExpr);
PropertyName *field = DotMember(ctorExpr);
@@ -3147,25 +3120,22 @@ CheckModuleGlobal(ModuleCompiler &m, Par
if (!CheckModuleLevelName(m, var, var->name()))
return false;
ParseNode *initNode = MaybeDefinitionInitializer(var);
if (!initNode)
return m.fail(var, "module import needs initializer");
- if (IsNumericLiteral(initNode))
+ if (IsNumericLiteral(m, initNode))
return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
- if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS))
+ if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS) || initNode->isKind(PNK_CALL))
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);
if (initNode->isKind(PNK_DOT))
return CheckGlobalDotImport(m, var->name(), initNode);
return m.fail(initNode, "unsupported import expression");
}
@@ -3251,28 +3221,31 @@ CheckArguments(FunctionCompiler &f, Pars
return true;
}
static bool
CheckFinalReturn(FunctionCompiler &f, ParseNode *stmt, RetType *retType)
{
if (stmt && stmt->isKind(PNK_RETURN)) {
if (ParseNode *coercionNode = UnaryKid(stmt)) {
- if (IsNumericLiteral(coercionNode)) {
- switch (ExtractNumericLiteral(coercionNode).which()) {
+ if (IsNumericLiteral(f.m(), coercionNode)) {
+ switch (ExtractNumericLiteral(f.m(), coercionNode).which()) {
case NumLit::BigUnsigned:
case NumLit::OutOfRangeInt:
return f.fail(coercionNode, "returned literal is out of integer range");
case NumLit::Fixnum:
case NumLit::NegativeInt:
*retType = RetType::Signed;
break;
case NumLit::Double:
*retType = RetType::Double;
break;
+ case NumLit::Float:
+ *retType = RetType::Float;
+ break;
}
return true;
}
AsmJSCoercion coercion;
if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion))
return false;
@@ -3299,47 +3272,24 @@ CheckVariable(FunctionCompiler &f, Parse
if (!CheckIdentifier(f.m(), var, name))
return false;
ParseNode *initNode = MaybeDefinitionInitializer(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 = nullptr;
- 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))
+ if (!IsNumericLiteral(f.m(), initNode))
return f.failName(initNode, "initializer for '%s' needs to be a numeric literal", name);
- NumLit literal = ExtractNumericLiteral(initNode);
- VarType type;
- switch (literal.which()) {
- case NumLit::Fixnum:
- case NumLit::NegativeInt:
- case NumLit::BigUnsigned:
- type = VarType::Int;
- break;
- case NumLit::Double:
- type = VarType::Double;
- break;
- case NumLit::OutOfRangeInt:
+ NumLit literal = ExtractNumericLiteral(f.m(), initNode);
+ if (!literal.hasType())
return f.failName(initNode, "initializer for '%s' is out of range", name);
- }
-
- return f.addVariable(var, name, type, literal.value());
+
+ return f.addVariable(var, name, literal.varType(), literal.value());
}
static bool
CheckVariables(FunctionCompiler &f, ParseNode **stmtIter)
{
ParseNode *stmt = *stmtIter;
for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) {
@@ -3354,31 +3304,22 @@ CheckVariables(FunctionCompiler &f, Pars
}
static bool
CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type);
static bool
CheckNumericLiteral(FunctionCompiler &f, ParseNode *num, MDefinition **def, Type *type)
{
- JS_ASSERT(IsNumericLiteral(num));
- NumLit literal = ExtractNumericLiteral(num);
-
- switch (literal.which()) {
- case NumLit::Fixnum:
- case NumLit::NegativeInt:
- case NumLit::BigUnsigned:
- case NumLit::Double:
- break;
- case NumLit::OutOfRangeInt:
+ NumLit literal = ExtractNumericLiteral(f.m(), num);
+ if (!literal.hasType())
return f.fail(num, "numeric literal out of representable integer range");
- }
*type = literal.type();
- *def = f.constant(literal.value());
+ *def = f.constant(literal.value(), literal.type());
return true;
}
static bool
CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *type)
{
PropertyName *name = varRef->name();
@@ -3386,17 +3327,17 @@ CheckVarRef(FunctionCompiler &f, ParseNo
*def = f.getLocalDef(*local);
*type = local->type.toType();
return true;
}
if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) {
switch (global->which()) {
case ModuleCompiler::Global::Constant:
- *def = f.constant(DoubleValue(global->constant()));
+ *def = f.constant(DoubleValue(global->constant()), Type::Double);
*type = Type::Double;
break;
case ModuleCompiler::Global::Variable:
*def = f.loadGlobalVar(*global);
*type = global->varType().toType();
break;
case ModuleCompiler::Global::Function:
case ModuleCompiler::Global::FFI:
@@ -3409,26 +3350,28 @@ CheckVarRef(FunctionCompiler &f, ParseNo
}
return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
}
static inline bool
IsLiteralOrConstInt(FunctionCompiler &f, ParseNode *pn, uint32_t *u32)
{
- if (IsLiteralInt(pn, u32))
+ if (IsLiteralInt(f.m(), pn, u32))
return true;
if (pn->getKind() != PNK_NAME)
return false;
PropertyName *name = pn->name();
const ModuleCompiler::Global *global = f.lookupGlobal(name);
- if (!global || global->which() != ModuleCompiler::Global::Variable ||
- !global->varIsLitConstant()) {
+ if (!global ||
+ global->which() != ModuleCompiler::Global::Variable ||
+ !global->varIsLitConstant())
+ {
return false;
}
const Value &v = global->litConstValue();
if (!v.isInt32())
return false;
*u32 = (uint32_t) v.toInt32();
@@ -3480,32 +3423,32 @@ CheckArrayAccess(FunctionCompiler &f, Pa
if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
return f.fail(indexExpr, "constant index out of range");
pointer <<= TypedArrayShift(*viewType);
// It is adequate to note pointer+1 rather than rounding up to the next
// access-size boundary because access is always aligned and the constraint
// will be rounded up to a larger alignment later.
f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1);
*needsBoundsCheck = NO_BOUNDS_CHECK;
- *def = f.constant(Int32Value(pointer));
+ *def = f.constant(Int32Value(pointer), Type::Int);
return true;
}
// Mask off the low bits to account for the clearing effect of a right shift
// followed by the left shift implicit in the array access. E.g., H32[i>>2]
// loses the low two bits.
int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
MDefinition *pointerDef;
if (indexExpr->isKind(PNK_RSH)) {
ParseNode *shiftNode = BinaryRight(indexExpr);
ParseNode *pointerNode = BinaryLeft(indexExpr);
uint32_t shift;
- if (!IsLiteralInt(shiftNode, &shift))
+ if (!IsLiteralInt(f.m(), shiftNode, &shift))
return f.failf(shiftNode, "shift amount must be constant");
unsigned requiredShift = TypedArrayShift(*viewType);
if (shift != requiredShift)
return f.failf(shiftNode, "shift amount must be %u", requiredShift);
if (pointerNode->isKind(PNK_BITAND))
FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
@@ -3513,17 +3456,17 @@ CheckArrayAccess(FunctionCompiler &f, Pa
// Fold a 'literal constant right shifted' now, and skip the bounds check if
// currently possible. This handles the optimization of many of these uses without
// the need for range analysis, and saves the generation of a MBitAnd op.
if (IsLiteralOrConstInt(f, pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
// Cases: b[c>>n], and b[(c&m)>>n]
pointer &= mask;
if (pointer < f.m().minHeapLength())
*needsBoundsCheck = NO_BOUNDS_CHECK;
- *def = f.constant(Int32Value(pointer));
+ *def = f.constant(Int32Value(pointer), Type::Int);
return true;
}
Type pointerType;
if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
return false;
if (!pointerType.isIntish())
@@ -3551,17 +3494,17 @@ CheckArrayAccess(FunctionCompiler &f, Pa
}
}
// Don't generate the mask op if there is no need for it which could happen for
// a shift of zero.
if (mask == -1)
*def = pointerDef;
else
- *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
+ *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask), Type::Int));
return true;
}
static bool
CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
{
ArrayBufferView::ViewType viewType;
@@ -3917,17 +3860,17 @@ CheckFuncPtrCall(FunctionCompiler &f, Pa
if (!indexExpr->isKind(PNK_BITAND))
return f.fail(indexExpr, "function-pointer table index expression needs & mask");
ParseNode *indexNode = BinaryLeft(indexExpr);
ParseNode *maskNode = BinaryRight(indexExpr);
uint32_t mask;
- if (!IsLiteralInt(maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
+ if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
return f.fail(maskNode, "function-pointer table index mask value must be a power of two");
MDefinition *indexDef;
Type indexType;
if (!CheckExpr(f, indexNode, &indexDef, &indexType))
return false;
if (!indexType.isIntish())
@@ -3979,35 +3922,21 @@ CheckFFICall(FunctionCompiler &f, ParseN
*type = retType.toType();
return true;
}
static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type);
static bool
-CheckFRoundArg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, const char* error)
-{
- ParseNode *arg = nullptr;
- if (!CheckFloat32Coercion(f.m(), expr, &arg, error))
- return false;
-
+CheckFRoundArg(FunctionCompiler &f, ParseNode *arg, MDefinition **def, Type *type)
+{
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())
@@ -4017,22 +3946,28 @@ CheckFRoundArg(FunctionCompiler &f, Pars
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)
-{
+CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type)
+{
+ ParseNode *argNode = nullptr;
+ if (!IsFloatCoercion(f.m(), callNode, &argNode))
+ return f.fail(callNode, "invalid call to fround");
+
MDefinition *operand;
Type operandType;
- if (!CheckFRoundArg(f, callNode, &operand, &operandType, "coercion to float should use fround"))
- return false;
+ if (!CheckFRoundArg(f, argNode, &operand, &operandType))
+ return false;
+
+ JS_ASSERT(operandType == Type::Float);
switch (retType.which()) {
case RetType::Double:
*def = f.unary<MToDouble>(operand);
*type = Type::Double;
return true;
case RetType::Signed:
*def = f.unary<MTruncateToInt32>(operand);
@@ -4071,17 +4006,17 @@ CheckMathBuiltinCall(FunctionCompiler &f
RetType retType, MDefinition **def, Type *type)
{
unsigned arity = 0;
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_fround: return CheckFRound(f, callNode, retType, def, type);
+ case AsmJSMathBuiltin_fround: return CheckMathFRound(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;
@@ -4352,30 +4287,31 @@ CheckConditional(FunctionCompiler &f, Pa
if (!f.joinIfElse(thenBlocks, elseExpr))
return false;
*def = f.popPhiOutput();
return true;
}
static bool
-IsValidIntMultiplyConstant(ParseNode *expr)
-{
- if (!IsNumericLiteral(expr))
- return false;
-
- NumLit literal = ExtractNumericLiteral(expr);
+IsValidIntMultiplyConstant(ModuleCompiler &m, ParseNode *expr)
+{
+ if (!IsNumericLiteral(m, expr))
+ return false;
+
+ NumLit literal = ExtractNumericLiteral(m, expr);
switch (literal.which()) {
case NumLit::Fixnum:
case NumLit::NegativeInt:
if (abs(literal.toInt32()) < (1<<20))
return true;
return false;
case NumLit::BigUnsigned:
case NumLit::Double:
+ case NumLit::Float:
case NumLit::OutOfRangeInt:
return false;
}
MOZ_ASSUME_UNREACHABLE("Bad literal");
}
static bool
@@ -4391,17 +4327,17 @@ CheckMultiply(FunctionCompiler &f, Parse
return false;
MDefinition *rhsDef;
Type rhsType;
if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
return false;
if (lhsType.isInt() && rhsType.isInt()) {
- if (!IsValidIntMultiplyConstant(lhs) && !IsValidIntMultiplyConstant(rhs))
+ if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
*def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
*type = Type::Intish;
return true;
}
if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
*def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
@@ -4589,26 +4525,27 @@ CheckBitwise(FunctionCompiler &f, ParseN
case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break;
case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break;
case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
case PNK_RSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
case PNK_URSH: identityElement = 0; onlyOnRight = true; *type = Type::Unsigned; break;
default: MOZ_ASSUME_UNREACHABLE("not a bitwise op");
}
- if (!onlyOnRight && IsBits32(lhs, identityElement)) {
+ uint32_t i;
+ if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) && i == uint32_t(identityElement)) {
Type rhsType;
if (!CheckExpr(f, rhs, def, &rhsType))
return false;
if (!rhsType.isIntish())
return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars());
return true;
}
- if (IsBits32(rhs, identityElement)) {
+ if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) {
if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL))
return CheckCall(f, lhs, RetType::Signed, def, type);
Type lhsType;
if (!CheckExpr(f, lhs, def, &lhsType))
return false;
if (!lhsType.isIntish())
return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars());
@@ -4641,34 +4578,37 @@ CheckBitwise(FunctionCompiler &f, ParseN
}
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);
+
+ ParseNode *arg;
+ if (!IsFloatCoercion(f.m(), expr, &arg)) {
+ return f.fail(expr, "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())");
+ }
+
+ return CheckFRoundArg(f, arg, def, type);
}
static bool
CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
{
JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
if (!f.mirGen().ensureBallast())
return false;
- if (IsNumericLiteral(expr))
+ if (IsNumericLiteral(f.m(), expr))
return CheckNumericLiteral(f, expr, def, type);
switch (expr->getKind()) {
case PNK_NAME: return CheckVarRef(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);
@@ -4787,17 +4727,17 @@ CheckFor(FunctionCompiler &f, ParseNode
if (maybeCond) {
Type condType;
if (!CheckExpr(f, maybeCond, &condDef, &condType))
return false;
if (!condType.isInt())
return f.failf(maybeCond, "%s is not a subtype of int", condType.toChars());
} else {
- condDef = f.constant(Int32Value(1));
+ condDef = f.constant(Int32Value(1), Type::Int);
}
MBasicBlock *afterLoop;
if (!f.branchAndStartLoopBody(condDef, &afterLoop, body, NextNode(forStmt)))
return false;
if (!CheckStatement(f, body))
return false;
@@ -4925,29 +4865,30 @@ CheckIf(FunctionCompiler &f, ParseNode *
}
return true;
}
static bool
CheckCaseExpr(FunctionCompiler &f, ParseNode *caseExpr, int32_t *value)
{
- if (!IsNumericLiteral(caseExpr))
+ if (!IsNumericLiteral(f.m(), caseExpr))
return f.fail(caseExpr, "switch case expression must be an integer literal");
- NumLit literal = ExtractNumericLiteral(caseExpr);
+ NumLit literal = ExtractNumericLiteral(f.m(), caseExpr);
switch (literal.which()) {
case NumLit::Fixnum:
case NumLit::NegativeInt:
*value = literal.toInt32();
break;
case NumLit::OutOfRangeInt:
case NumLit::BigUnsigned:
return f.fail(caseExpr, "switch case expression out of integer range");
case NumLit::Double:
+ case NumLit::Float:
return f.fail(caseExpr, "switch case expression must be an integer literal");
}
return true;
}
static bool
CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt)
@@ -5030,17 +4971,17 @@ CheckSwitch(FunctionCompiler &f, ParseNo
if (!cases.resize(tableLength))
return false;
MBasicBlock *switchBlock;
if (!f.startSwitch(switchStmt, exprDef, low, high, &switchBlock))
return false;
for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
- int32_t caseValue = ExtractNumericLiteral(CaseExpr(stmt)).toInt32();
+ int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32();
unsigned caseIndex = caseValue - low;
if (cases[caseIndex])
return f.fail(stmt, "no duplicate case labels");
if (!f.startSwitchCase(switchBlock, &cases[caseIndex], stmt))
return false;
@@ -5256,17 +5197,17 @@ CheckFunction(ModuleCompiler &m, LifoAll
RetType retType;
if (!CheckFinalReturn(f, lastNonEmptyStmt, &retType))
return false;
if (!CheckReturnType(f, lastNonEmptyStmt, retType))
return false;
Signature sig(Move(argTypes), retType);
- ModuleCompiler::Func *func;
+ ModuleCompiler::Func *func = nullptr;
if (!CheckFunctionSignature(m, fn, Move(sig), FunctionName(fn), &func))
return false;
if (func->defined())
return m.failName(fn, "function '%s' already defined", FunctionName(fn));
func->define(fn->pn_pos.begin);
func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);