Bug 878488: Implement asm.js Math constants; r=luke

This commit is contained in:
Benjamin Bouvier 2014-02-04 16:11:54 +01:00
parent 7f7dcddee5
commit 491fbb4542
7 changed files with 164 additions and 72 deletions

View File

@ -98,3 +98,4 @@ assertEq(asmLink(asmCompileCached("g","ffis", USE_ASM + "var x=ffis.x|0; functio
var i32 = new Int32Array(4096);
i32[4] = 42;
assertEq(asmLink(asmCompileCached("g","ffis","buf", USE_ASM + "var i32=new g.Int32Array(buf); function f(i) { i=i|0; return i32[i>>2]|0 } return f"), this, null, i32.buffer)(4*4), 42);
assertEq(asmLink(asmCompileCached('glob', USE_ASM + 'var x=glob.Math.PI; function f() { return +x } return f'), this)(), Math.PI);

View File

@ -86,3 +86,17 @@ assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|
assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); } return f');
assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; +abs(i|0); } return f');
assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; abs(d)|0; } return f');
assertAsmTypeFail('glob', USE_ASM + 'var tau=glob.Math.TAU; function f() {} return f');
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return pi | 0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return +pi() } return f');
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { pi = +3; } return f');
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {PI: Math.cos}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {PI: Math.SQRT2}});
for (var c of ['E', 'LN10', 'LN2', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2']) {
var f = asmLink(asmCompile('glob', USE_ASM + 'var x=glob.Math.' + c +'; function f() { return +x } return f'), this);
assertEq(f(), eval('Math.' + c));
}

View File

@ -12,6 +12,7 @@
# include "vtune/VTuneWrapper.h"
#endif
#include "jsmath.h"
#include "jsprf.h"
#include "jsworkers.h"
#include "prmjtime.h"
@ -936,7 +937,7 @@ class MOZ_STACK_CLASS ModuleCompiler
FuncPtrTable,
FFI,
ArrayView,
MathBuiltin
MathBuiltinFunction
};
private:
@ -951,7 +952,7 @@ class MOZ_STACK_CLASS ModuleCompiler
uint32_t funcPtrTableIndex_;
uint32_t ffiIndex_;
ArrayBufferView::ViewType viewType_;
AsmJSMathBuiltin mathBuiltin_;
AsmJSMathBuiltinFunction mathBuiltinFunc_;
} u;
friend class ModuleCompiler;
@ -994,9 +995,9 @@ class MOZ_STACK_CLASS ModuleCompiler
JS_ASSERT(which_ == ArrayView);
return u.viewType_;
}
AsmJSMathBuiltin mathBuiltin() const {
JS_ASSERT(which_ == MathBuiltin);
return u.mathBuiltin_;
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
JS_ASSERT(which_ == MathBuiltinFunction);
return u.mathBuiltinFunc_;
}
};
@ -1063,6 +1064,25 @@ class MOZ_STACK_CLASS ModuleCompiler
typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
struct MathBuiltin
{
enum Kind { Function, Constant };
Kind kind;
union {
double cst;
AsmJSMathBuiltinFunction func;
} u;
MathBuiltin() : kind(Kind(-1)) {}
MathBuiltin(double cst) : kind(Constant) {
u.cst = cst;
}
MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
u.func = func;
}
};
private:
struct SlowFunction
{
@ -1072,7 +1092,7 @@ class MOZ_STACK_CLASS ModuleCompiler
unsigned column;
};
typedef HashMap<PropertyName*, AsmJSMathBuiltin> MathNameMap;
typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
typedef HashMap<PropertyName*, Global*> GlobalMap;
typedef js::Vector<Func*> FuncVector;
typedef js::Vector<AsmJSGlobalAccess> GlobalAccessVector;
@ -1106,10 +1126,18 @@ class MOZ_STACK_CLASS ModuleCompiler
DebugOnly<bool> finishedFunctionBodies_;
bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) {
bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) {
JSAtom *atom = Atomize(cx_, name, strlen(name));
if (!atom)
return false;
MathBuiltin builtin(func);
return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
}
bool addStandardLibraryMathName(const char *name, double cst) {
JSAtom *atom = Atomize(cx_, name, strlen(name));
if (!atom)
return false;
MathBuiltin builtin(cst);
return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
}
@ -1175,7 +1203,15 @@ class MOZ_STACK_CLASS ModuleCompiler
!addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
!addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
!addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround))
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
!addStandardLibraryMathName("E", M_E) ||
!addStandardLibraryMathName("LN10", M_LN10) ||
!addStandardLibraryMathName("LN2", M_LN2) ||
!addStandardLibraryMathName("LOG2E", M_LOG2E) ||
!addStandardLibraryMathName("LOG10E", M_LOG10E) ||
!addStandardLibraryMathName("PI", M_PI) ||
!addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
!addStandardLibraryMathName("SQRT2", M_SQRT2))
{
return false;
}
@ -1289,7 +1325,7 @@ class MOZ_STACK_CLASS ModuleCompiler
FuncPtrTable &funcPtrTable(unsigned i) {
return funcPtrTables_[i];
}
bool lookupStandardLibraryMathName(PropertyName *name, AsmJSMathBuiltin *mathBuiltin) const {
bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const {
if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
*mathBuiltin = p->value();
return true;
@ -1390,18 +1426,17 @@ class MOZ_STACK_CLASS ModuleCompiler
global->u.viewType_ = vt;
return globals_.putNew(varName, global);
}
bool addMathBuiltin(PropertyName *varName, AsmJSMathBuiltin mathBuiltin, PropertyName *fieldName) {
if (!module_->addMathBuiltin(mathBuiltin, fieldName))
bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) {
if (!module_->addMathBuiltinFunction(func, fieldName))
return false;
Global *global = moduleLifo_.new_<Global>(Global::MathBuiltin);
Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
if (!global)
return false;
global->u.mathBuiltin_ = mathBuiltin;
global->u.mathBuiltinFunc_ = func;
return globals_.putNew(varName, global);
}
bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
if (!module_->addGlobalConstant(constant, fieldName))
return false;
private:
bool addGlobalDoubleConstant(PropertyName *varName, double constant) {
Global *global = moduleLifo_.new_<Global>(Global::ConstantLiteral);
if (!global)
return false;
@ -1409,6 +1444,17 @@ class MOZ_STACK_CLASS ModuleCompiler
global->u.varOrConst.type_ = VarType::Double;
return globals_.putNew(varName, global);
}
public:
bool addMathBuiltinConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
if (!module_->addMathBuiltinConstant(constant, fieldName))
return false;
return addGlobalDoubleConstant(varName, constant);
}
bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
if (!module_->addGlobalConstant(constant, fieldName))
return false;
return addGlobalDoubleConstant(varName, constant);
}
bool addExportedFunction(const Func *func, PropertyName *maybeFieldName) {
AsmJSModule::ArgCoercionVector argCoercions;
const VarTypeVector &args = func->sig().args();
@ -1774,8 +1820,8 @@ IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr)
const ModuleCompiler::Global *global = m.lookupGlobal(callee->name());
if (!global ||
global->which() != ModuleCompiler::Global::MathBuiltin ||
global->mathBuiltin() != AsmJSMathBuiltin_fround)
global->which() != ModuleCompiler::Global::MathBuiltinFunction ||
global->mathBuiltinFunction() != AsmJSMathBuiltin_fround)
{
return false;
}
@ -3104,11 +3150,19 @@ CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNo
if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math)
return m.fail(base, "expecting global.Math");
AsmJSMathBuiltin mathBuiltin;
ModuleCompiler::MathBuiltin mathBuiltin;
if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
return m.failName(initNode, "'%s' is not a standard Math builtin", field);
return m.addMathBuiltin(varName, mathBuiltin, field);
switch (mathBuiltin.kind) {
case ModuleCompiler::MathBuiltin::Function:
return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
case ModuleCompiler::MathBuiltin::Constant:
return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
default:
break;
}
MOZ_ASSUME_UNREACHABLE("unexpected or uninitialized math builtin type");
}
if (IsUseOfName(base, m.module().globalArgumentName())) {
@ -3366,7 +3420,7 @@ CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *typ
break;
case ModuleCompiler::Global::Function:
case ModuleCompiler::Global::FFI:
case ModuleCompiler::Global::MathBuiltin:
case ModuleCompiler::Global::MathBuiltinFunction:
case ModuleCompiler::Global::FuncPtrTable:
case ModuleCompiler::Global::ArrayView:
return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
@ -4021,12 +4075,12 @@ CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type)
}
static bool
CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin mathBuiltin,
CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
RetType retType, MDefinition **def, Type *type)
{
unsigned arity = 0;
AsmJSImmKind doubleCallee, floatCallee;
switch (mathBuiltin) {
switch (func) {
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);
@ -4043,7 +4097,7 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin
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;
default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin");
default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin function");
}
if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid)
@ -4088,8 +4142,8 @@ CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **d
switch (global->which()) {
case ModuleCompiler::Global::FFI:
return CheckFFICall(f, call, global->ffiIndex(), retType, def, type);
case ModuleCompiler::Global::MathBuiltin:
return CheckMathBuiltinCall(f, call, global->mathBuiltin(), retType, def, type);
case ModuleCompiler::Global::MathBuiltinFunction:
return CheckMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
case ModuleCompiler::Global::ConstantLiteral:
case ModuleCompiler::Global::ConstantImport:
case ModuleCompiler::Global::Variable:

View File

@ -143,7 +143,7 @@ ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue global
}
static bool
ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
{
RootedValue v(cx);
if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
@ -153,7 +153,7 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob
return false;
Native native = nullptr;
switch (global.mathBuiltin()) {
switch (global.mathBuiltinFunction()) {
case AsmJSMathBuiltin_sin: native = math_sin; break;
case AsmJSMathBuiltin_cos: native = math_cos; break;
case AsmJSMathBuiltin_tan: native = math_tan; break;
@ -173,21 +173,26 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob
}
if (!IsNativeFunction(v, native))
return LinkFail(cx, "bad Math.* builtin");
return LinkFail(cx, "bad Math.* builtin function");
return true;
}
static bool
ValidateGlobalConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
{
RootedPropertyName field(cx, global.constantName());
RootedValue v(cx);
if (!GetDataProperty(cx, globalVal, field, &v))
return false;
RootedValue v(cx, globalVal);
if (global.constantKind() == AsmJSModule::Global::MathConstant) {
if (!GetDataProperty(cx, v, cx->names().Math, &v))
return false;
}
if (!GetDataProperty(cx, v, field, &v))
return false;
if (!v.isNumber())
return LinkFail(cx, "global constant value needs to be a number");
return LinkFail(cx, "math / global constant value needs to be a number");
// NaN != NaN
if (IsNaN(global.constantValue())) {
@ -269,12 +274,12 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
if (!ValidateArrayView(cx, global, globalVal, bufferVal))
return false;
break;
case AsmJSModule::Global::MathBuiltin:
if (!ValidateMathBuiltin(cx, global, globalVal))
case AsmJSModule::Global::MathBuiltinFunction:
if (!ValidateMathBuiltinFunction(cx, global, globalVal))
return false;
break;
case AsmJSModule::Global::Constant:
if (!ValidateGlobalConstant(cx, global, globalVal))
if (!ValidateConstant(cx, global, globalVal))
return false;
break;
}

View File

@ -34,7 +34,7 @@ enum AsmJSCoercion
};
// The asm.js spec recognizes this set of builtin Math functions.
enum AsmJSMathBuiltin
enum AsmJSMathBuiltinFunction
{
AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
@ -97,8 +97,9 @@ class AsmJSModule
class Global
{
public:
enum Which { Variable, FFI, ArrayView, MathBuiltin, Constant };
enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant };
enum VarInitKind { InitConstant, InitImport };
enum ConstantKind { GlobalConstant, MathConstant };
private:
struct Pod {
@ -114,8 +115,11 @@ class AsmJSModule
} var;
uint32_t ffiIndex_;
ArrayBufferView::ViewType viewType_;
AsmJSMathBuiltin mathBuiltin_;
double constantValue_;
AsmJSMathBuiltinFunction mathBuiltinFunc_;
struct {
ConstantKind kind_;
double value_;
} constant;
} u;
} pod;
PropertyName *name_;
@ -179,20 +183,24 @@ class AsmJSModule
return pod.u.viewType_;
}
PropertyName *mathName() const {
JS_ASSERT(pod.which_ == MathBuiltin);
JS_ASSERT(pod.which_ == MathBuiltinFunction);
return name_;
}
AsmJSMathBuiltin mathBuiltin() const {
JS_ASSERT(pod.which_ == MathBuiltin);
return pod.u.mathBuiltin_;
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
JS_ASSERT(pod.which_ == MathBuiltinFunction);
return pod.u.mathBuiltinFunc_;
}
PropertyName *constantName() const {
JS_ASSERT(pod.which_ == Constant);
return name_;
}
ConstantKind constantKind() const {
JS_ASSERT(pod.which_ == Constant);
return pod.u.constant.kind_;
}
double constantValue() const {
JS_ASSERT(pod.which_ == Constant);
return pod.u.constantValue_;
return pod.u.constant.value_;
}
size_t serializedSize() const;
@ -488,14 +496,21 @@ class AsmJSModule
g.pod.u.viewType_ = vt;
return globals_.append(g);
}
bool addMathBuiltin(AsmJSMathBuiltin mathBuiltin, PropertyName *field) {
Global g(Global::MathBuiltin, field);
g.pod.u.mathBuiltin_ = mathBuiltin;
bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) {
Global g(Global::MathBuiltinFunction, field);
g.pod.u.mathBuiltinFunc_ = func;
return globals_.append(g);
}
bool addMathBuiltinConstant(double value, PropertyName *field) {
Global g(Global::Constant, field);
g.pod.u.constant.value_ = value;
g.pod.u.constant.kind_ = Global::MathConstant;
return globals_.append(g);
}
bool addGlobalConstant(double value, PropertyName *name) {
Global g(Global::Constant, name);
g.pod.u.constantValue_ = value;
g.pod.u.constant.value_ = value;
g.pod.u.constant.kind_ = Global::GlobalConstant;
return globals_.append(g);
}
bool addFuncPtrTable(unsigned numElems, uint32_t *globalDataOffset) {

View File

@ -49,28 +49,6 @@ using mozilla::SpecificNaN;
using JS::ToNumber;
using JS::GenericNaN;
#ifndef M_E
#define M_E 2.7182818284590452354
#endif
#ifndef M_LOG2E
#define M_LOG2E 1.4426950408889634074
#endif
#ifndef M_LOG10E
#define M_LOG10E 0.43429448190325182765
#endif
#ifndef M_LN2
#define M_LN2 0.69314718055994530942
#endif
#ifndef M_LN10
#define M_LN10 2.30258509299404568402
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440
#endif
static const JSConstDoubleSpec math_constants[] = {
{M_E, "E", 0, {0,0,0}},
{M_LOG2E, "LOG2E", 0, {0,0,0}},

View File

@ -11,6 +11,31 @@
#include "NamespaceImports.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#ifndef M_E
# define M_E 2.7182818284590452354
#endif
#ifndef M_LOG2E
# define M_LOG2E 1.4426950408889634074
#endif
#ifndef M_LOG10E
# define M_LOG10E 0.43429448190325182765
#endif
#ifndef M_LN2
# define M_LN2 0.69314718055994530942
#endif
#ifndef M_LN10
# define M_LN10 2.30258509299404568402
#endif
#ifndef M_SQRT2
# define M_SQRT2 1.41421356237309504880
#endif
#ifndef M_SQRT1_2
# define M_SQRT1_2 0.70710678118654752440
#endif
namespace js {
typedef double (*UnaryFunType)(double);