Bug 685150 - Generate native code for Math.min/Math.max in the methodjit. r=bhackett
authorTom Schuster <evilpies@gmail.com>
Fri, 09 Sep 2011 14:53:28 +0200
changeset 77422 fd47df5b784a2fcb5d0b8fd335c618ef9b02600f
parent 77421 35e3a5f3c07baa08ed2b9d047817bbfa31d3db18
child 77423 95015f02ddc7eafd1675f8b84985534fc6d284a8
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersbhackett
bugs685150
milestone9.0a1
Bug 685150 - Generate native code for Math.min/Math.max in the methodjit. r=bhackett
js/src/jit-test/tests/basic/testMathMinMax.js
js/src/methodjit/Compiler.h
js/src/methodjit/FastBuiltins.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testMathMinMax.js
@@ -0,0 +1,74 @@
+for (var i = 2; i < 10; i++) {
+    assertEq(Math.min(i, 1), 1);
+    assertEq(Math.min(i, -1), -1);
+    assertEq(Math.min(1, i), 1);
+    assertEq(Math.min(-1, i), -1);
+    assertEq(Math.min(5, 2), 2);
+    assertEq(Math.min(2, 5), 2);
+    assertEq(Math.min(5, -2), -2);
+    assertEq(Math.min(-2, 5), -2);
+}
+
+for (i = 2; i < 10; i++) {
+    assertEq(Math.max(i, 1), i);
+    assertEq(Math.max(i, -1), i);
+    assertEq(Math.max(1, i), i);
+    assertEq(Math.max(-1, i), i);
+    assertEq(Math.max(5, -2), 5);
+    assertEq(Math.max(-2, 5), 5);
+    assertEq(Math.max(5, 2), 5);
+    assertEq(Math.max(2, 5), 5);
+}
+
+for (i = 2.1; i < 13; i += 3.17584) {
+    assertEq(Math.max(i, 1), i);
+    assertEq(Math.max(i, 1.5), i);
+    assertEq(Math.max(1, i), i);
+    assertEq(Math.max(1.5, i), i);
+    
+    assertEq(Math.max(NaN, NaN), NaN);
+    assertEq(Math.max(NaN, Infinity), NaN);
+    assertEq(Math.max(Infinity, NaN), NaN);
+    
+    assertEq(Math.max(NaN, i), NaN);
+    assertEq(Math.max(i, NaN), NaN);
+    
+    assertEq(Math.max(i, Infinity), Infinity);
+    assertEq(Math.max(Infinity, i), Infinity);
+    
+    assertEq(Math.max(i, -Infinity), i);
+    assertEq(Math.max(-Infinity, i), i);    
+}
+
+for (i = 2.1; i < 13; i += 3.17584) {
+    assertEq(Math.min(i, 1), 1);
+    assertEq(Math.min(i, 1.5), 1.5);
+    assertEq(Math.min(1, i), 1);
+    assertEq(Math.min(1.5, i), 1.5);
+    
+    assertEq(Math.min(NaN, NaN), NaN);
+    assertEq(Math.min(NaN, Infinity), NaN);
+    assertEq(Math.min(Infinity, NaN), NaN);
+    
+    assertEq(Math.min(NaN, i), NaN);
+    assertEq(Math.min(i, NaN), NaN);
+    
+    assertEq(Math.min(i, Infinity), i);
+    assertEq(Math.min(Infinity, i), i);
+    
+    assertEq(Math.min(i, -Infinity), -Infinity);
+    assertEq(Math.min(-Infinity, i), -Infinity);
+}
+
+function isNegZero(n) {
+    return n === 0 && 1/n === -Infinity;
+}
+
+for (i = 0; i < 5; i++) {
+    assertEq(isNegZero(Math.min(0, -0)), true);
+    assertEq(isNegZero(Math.min(-0, 0)), true);
+    assertEq(isNegZero(Math.min(-0, -0)), true);
+    assertEq(isNegZero(Math.max(0, -0)), false);
+    assertEq(isNegZero(Math.max(-0, 0)), false);
+    assertEq(isNegZero(Math.max(-0, -0)), true);
+}
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -758,16 +758,20 @@ class Compiler : public BaseCompiler
     /* Fast builtins. */
     JSObject *pushedSingleton(unsigned pushed);
     CompileStatus callArrayBuiltin(uint32 argc, bool callingNew);
     CompileStatus inlineNativeFunction(uint32 argc, bool callingNew);
     CompileStatus inlineScriptedFunction(uint32 argc, bool callingNew);
     CompileStatus compileMathAbsInt(FrameEntry *arg);
     CompileStatus compileMathAbsDouble(FrameEntry *arg);
     CompileStatus compileMathSqrt(FrameEntry *arg);
+    CompileStatus compileMathMinMaxDouble(FrameEntry *arg1, FrameEntry *arg2, 
+                                          Assembler::DoubleCondition cond); 
+    CompileStatus compileMathMinMaxInt(FrameEntry *arg1, FrameEntry *arg2, 
+                                       Assembler::Condition cond);                                       
     CompileStatus compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2);
     CompileStatus compileArrayPush(FrameEntry *thisv, FrameEntry *arg);
     CompileStatus compileArrayPop(FrameEntry *thisv, bool isPacked);
     CompileStatus compileArrayWithLength(uint32 argc);
     CompileStatus compileArrayWithArgs(uint32 argc);
 
     enum RoundingMode { Floor, Round };
     CompileStatus compileRound(FrameEntry *arg, RoundingMode mode);
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -171,16 +171,110 @@ mjit::Compiler::compileMathSqrt(FrameEnt
 
     frame.popn(3);
     frame.pushDouble(fpResultReg);
 
     return Compile_Okay;
 }
 
 CompileStatus
+mjit::Compiler::compileMathMinMaxDouble(FrameEntry *arg1, FrameEntry *arg2, 
+                                        Assembler::DoubleCondition cond)
+{
+    FPRegisterID fpReg1;
+    FPRegisterID fpReg2;
+    bool allocate;
+
+    DebugOnly<MaybeJump> notNumber = loadDouble(arg1, &fpReg1, &allocate);
+    JS_ASSERT(!((MaybeJump)notNumber).isSet());
+
+    if (!allocate) {
+        FPRegisterID fpResultReg = frame.allocFPReg();
+        masm.moveDouble(fpReg1, fpResultReg);
+        fpReg1 = fpResultReg;
+    }
+
+    DebugOnly<MaybeJump> notNumber2 = loadDouble(arg2, &fpReg2, &allocate);
+    JS_ASSERT(!((MaybeJump)notNumber2).isSet());
+
+
+    /* Slow path for 0 and NaN, because they have special requriments. */
+    masm.zeroDouble(Registers::FPConversionTemp);
+    Jump zeroOrNan = masm.branchDouble(Assembler::DoubleEqualOrUnordered, fpReg1, 
+                                       Registers::FPConversionTemp);
+    stubcc.linkExit(zeroOrNan, Uses(4));
+    Jump zeroOrNan2 = masm.branchDouble(Assembler::DoubleEqualOrUnordered, fpReg2, 
+                                        Registers::FPConversionTemp);
+    stubcc.linkExit(zeroOrNan2, Uses(4));
+
+
+    Jump ifTrue = masm.branchDouble(cond, fpReg1, fpReg2);
+    masm.moveDouble(fpReg2, fpReg1);
+
+    ifTrue.linkTo(masm.label(), &masm);
+
+    if (allocate)
+        frame.freeReg(fpReg2);
+
+    stubcc.leave();
+    stubcc.masm.move(Imm32(2), Registers::ArgReg1);
+    OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
+
+    frame.popn(4);
+    frame.pushDouble(fpReg1);
+
+    stubcc.rejoin(Changes(1));
+    return Compile_Okay;
+}
+
+CompileStatus
+mjit::Compiler::compileMathMinMaxInt(FrameEntry *arg1, FrameEntry *arg2, Assembler::Condition cond)
+{
+    /* Get this case out of the way */
+    if (arg1->isConstant() && arg2->isConstant()) {
+        int32 a = arg1->getValue().toInt32();
+        int32 b = arg2->getValue().toInt32();
+
+        frame.popn(4);
+        if (cond == Assembler::LessThan)
+            frame.push(Int32Value(a < b ? a : b));
+        else
+            frame.push(Int32Value(a > b ? a : b));
+        return Compile_Okay;
+    }
+
+    Jump ifTrue;
+    RegisterID reg;
+    if (arg1->isConstant()) {
+        reg = frame.copyDataIntoReg(arg2);
+        int32_t v = arg1->getValue().toInt32();
+
+        ifTrue = masm.branch32(cond, reg, Imm32(v));
+        masm.move(Imm32(v), reg);
+    } else if (arg2->isConstant()) {
+        reg = frame.copyDataIntoReg(arg1);
+        int32_t v = arg2->getValue().toInt32();
+
+        ifTrue = masm.branch32(cond, reg, Imm32(v));
+        masm.move(Imm32(v), reg);
+    } else {
+        reg = frame.copyDataIntoReg(arg1);
+        RegisterID regB = frame.tempRegForData(arg2);
+
+        ifTrue = masm.branch32(cond, reg, regB);
+        masm.move(regB, reg);
+    }
+
+    ifTrue.linkTo(masm.label(), &masm);
+    frame.popn(4);
+    frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
+    return Compile_Okay;
+}
+
+CompileStatus
 mjit::Compiler::compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2)
 {
     FPRegisterID fpScratchReg = frame.allocFPReg();
     FPRegisterID fpResultReg = frame.allocFPReg();
 
     FPRegisterID fpReg;
     bool allocate;
 
@@ -651,12 +745,27 @@ mjit::Compiler::inlineNativeFunction(uin
         if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE &&
             (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) &&
             arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant())
         {
             Value arg2Value = arg2->getValue();
             if (arg2Value.toDouble() == -0.5 || arg2Value.toDouble() == 0.5)
                 return compileMathPowSimple(arg1, arg2);
         }
+        if ((native == js_math_min || native == js_math_max)) {
+            if (arg1Type == JSVAL_TYPE_INT32 && arg2Type == JSVAL_TYPE_INT32 &&
+                type == JSVAL_TYPE_INT32) {
+                return compileMathMinMaxInt(arg1, arg2, 
+                        native == js_math_min ? Assembler::LessThan : Assembler::GreaterThan);
+            }
+            if ((arg1Type == JSVAL_TYPE_INT32 || arg1Type == JSVAL_TYPE_DOUBLE) &&
+                (arg2Type == JSVAL_TYPE_INT32 || arg2Type == JSVAL_TYPE_DOUBLE) &&
+                type == JSVAL_TYPE_DOUBLE) {
+                return compileMathMinMaxDouble(arg1, arg2,
+                        (native == js_math_min)
+                        ? Assembler::DoubleLessThan
+                        : Assembler::DoubleGreaterThan);
+            }
+        }
     }
     return Compile_InlineAbort;
 }