Bug 966295: Implement variadic Math.min and Math.max in Odin; r=luke

This commit is contained in:
Benjamin Bouvier 2014-02-11 14:19:59 +01:00
Родитель 8829c682ee
Коммит 1360c091f9
4 изменённых файлов: 97 добавлений и 5 удалений

Просмотреть файл

@ -59,8 +59,9 @@ var f = asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(
for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)])
assertEq(f(n), Math.abs(n|0)|0);
function testBinary(f, g) {
var numbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
var intNumbers = [-10000, -3, -1, 0, 3, 10000];
function testBinary(f, g, numbers) {
for (n of numbers)
for (o of numbers)
assertEq(f(n,o), g(n,o));
@ -68,11 +69,43 @@ function testBinary(f, g) {
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow, doubleNumbers);
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
assertAsmTypeFail('glob', USE_ASM + 'var f32=glob.Math.fround; var min=glob.Math.min; function f(d) { d=f32(d); return +min(d, f32(5)) } return f');
assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
function testTernary(f, g, numbers) {
for (n of numbers)
for (o of numbers)
for (p of numbers)
assertEq(f(n,o,p), g(n,o,p));
}
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=g|0; return +min(d,e,g) } return f');
assertAsmTypeFail('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=+g; return max(d,e,g)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return min(d,e,g)|0 } return f');
assertAsmTypeFail('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f');
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); } return f');
assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; var i=0; i = sin(d)|0; } return f');

Просмотреть файл

@ -1204,6 +1204,8 @@ class MOZ_STACK_CLASS ModuleCompiler
!addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
!addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
!addStandardLibraryMathName("min", AsmJSMathBuiltin_min) ||
!addStandardLibraryMathName("max", AsmJSMathBuiltin_max) ||
!addStandardLibraryMathName("E", M_E) ||
!addStandardLibraryMathName("LN10", M_LN10) ||
!addStandardLibraryMathName("LN2", M_LN2) ||
@ -2209,6 +2211,14 @@ class FunctionCompiler
return ins;
}
MDefinition *minMax(MDefinition *lhs, MDefinition *rhs, MIRType type, bool isMax) {
if (!curBlock_)
return nullptr;
MMinMax *ins = MMinMax::New(alloc(), lhs, rhs, type, isMax);
curBlock_->add(ins);
return ins;
}
MDefinition *mul(MDefinition *lhs, MDefinition *rhs, MIRType type, MMul::Mode mode)
{
if (!curBlock_)
@ -3792,6 +3802,51 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition
return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
}
static bool
CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type, bool isMax)
{
if (CallArgListLength(callNode) < 2)
return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
ParseNode *firstArg = CallArgList(callNode);
MDefinition *firstDef;
Type firstType;
if (!CheckExpr(f, firstArg, &firstDef, &firstType))
return false;
bool opIsDouble = firstType.isMaybeDouble();
bool opIsInteger = firstType.isInt();
MIRType opType = firstType.toMIRType();
if (!opIsDouble && !opIsInteger)
return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars());
MDefinition *lastDef = firstDef;
ParseNode *nextArg = NextNode(firstArg);
for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
MDefinition *nextDef;
Type nextType;
if (!CheckExpr(f, nextArg, &nextDef, &nextType))
return false;
if (opIsDouble && !nextType.isMaybeDouble())
return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars());
if (opIsInteger && !nextType.isInt())
return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars());
lastDef = f.minMax(lastDef, nextDef, opType, isMax);
}
if (opIsDouble && retType != RetType::Double)
return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars());
if (opIsInteger && retType != RetType::Signed)
return f.failf(callNode, "return type is int, used as %s", retType.toType().toChars());
*type = opIsDouble ? Type::Double : Type::Signed;
*def = lastDef;
return true;
}
typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
static bool
@ -4085,6 +4140,8 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinF
case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type);
case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type);
case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, retType, def, type);
case AsmJSMathBuiltin_min: return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ false);
case AsmJSMathBuiltin_max: return CheckMathMinMax(f, callNode, retType, def, type, /* isMax = */ true);
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;

Просмотреть файл

@ -166,6 +166,8 @@ ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleVa
case AsmJSMathBuiltin_log: native = math_log; break;
case AsmJSMathBuiltin_pow: native = js_math_pow; break;
case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break;
case AsmJSMathBuiltin_min: native = js_math_min; break;
case AsmJSMathBuiltin_max: native = js_math_max; break;
case AsmJSMathBuiltin_abs: native = js_math_abs; break;
case AsmJSMathBuiltin_atan2: native = math_atan2; break;
case AsmJSMathBuiltin_imul: native = math_imul; break;

Просмотреть файл

@ -41,7 +41,7 @@ enum AsmJSMathBuiltinFunction
AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
AsmJSMathBuiltin_fround
AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max
};
// Static-link data is used to patch a module either after it has been