[JSC] Unify Math.pow() accross all tiers
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Apr 2016 20:50:08 +0000 (20:50 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Apr 2016 20:50:08 +0000 (20:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157121

Patch by Benjamin Poulain <bpoulain@apple.com> on 2016-04-28
Reviewed by Geoffrey Garen.

My previous optimizations of DFG compile time have slowly
regressed Sunspider's math-partial-sums.

What is happenning is baseline used a thunk for Math.pow()
that has a special case for an exponent of -0.5, while
DFG/FTL have other special cases for other exponents.
The faster we get to DFG, the less time we spend in that fast
case for -0.5.

While looking into this, I discovered some correctness issues. Baseline
optimizes y=-0.5 by turning it into 1/sqrt(). DFG/FTL optimize constant
y=0.5 by turning it into sqrt(). The problem is sqrt() behaves differently
for -0 and -Infinity. With sqrt(), negative numbers are undefined,
and the result is NaN. With pow(), they have a result.

Something else that has bothered me for a while is that Math.pow()
with the same arguments give you different results in LLINT, Baseline,
and DFG/FTL. This seems a bit dangerous for numerical stability.

With this patch, I unify the behaviors for all tiers while keeping
the "special cases".

We have pow() that is super slow, but most callers don't need the
full power. We have:
-pow() with an exponent between 0 and 1000 is a fast path implemented
 by multiplication only.
-pow(x, 0.5) is sqrt with special checks for negative values.
-pow(x, -0.5) is sqrt with special checks for negative values.

The C++ implementation handles all those optimizations too. This ensure
you get the same results from LLINT to FTL.

The thunk is eliminated, it was producing incorrect results and only
optimized Sunspider's partial-sums.

DFG gets the optimized integer, 0.5 and -0.5 cases since those are important
for somewhat-hot code. DFG falls back to the C++ code for any non-obvious case.

FTL gets the full C++ implementation inlined in B3. B3 knows how to eliminate
all the dead cases so you get the best if your code is hot enough to reach FTL.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode): Deleted.
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToArithSqrt): Deleted.
* dfg/DFGNodeType.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::compileArithPowIntegerFastPath):
(JSC::DFG::SpeculativeJIT::compileArithPow):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileArithPow):
* jit/ThunkGenerators.cpp:
(JSC::powThunkGenerator): Deleted.
* jit/ThunkGenerators.h:
* runtime/MathCommon.cpp:
(JSC::operationMathPow):
* runtime/MathCommon.h:
* runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic): Deleted.
* tests/stress/math-pow-stable-results.js: Added.
Getting consistent results when tiering up is new.
This test verify that results always remains the same as LLINT.

* tests/stress/math-pow-with-constants.js:
(testPowUsedAsSqrt):
(powUsedAsOneOverSqrt):
(testPowUsedAsOneOverSqrt):
(powUsedAsSquare):
(testPowUsedAsSquare):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@200208 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jit/ThunkGenerators.h
Source/JavaScriptCore/runtime/MathCommon.cpp
Source/JavaScriptCore/runtime/MathCommon.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/tests/stress/math-pow-stable-results.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/math-pow-with-constants.js

index 5dcb312..45a66b3 100644 (file)
@@ -1,3 +1,82 @@
+2016-04-28  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Unify Math.pow() accross all tiers
+        https://bugs.webkit.org/show_bug.cgi?id=157121
+
+        Reviewed by Geoffrey Garen.
+
+        My previous optimizations of DFG compile time have slowly
+        regressed Sunspider's math-partial-sums.
+
+        What is happenning is baseline used a thunk for Math.pow()
+        that has a special case for an exponent of -0.5, while
+        DFG/FTL have other special cases for other exponents.
+        The faster we get to DFG, the less time we spend in that fast
+        case for -0.5.
+
+        While looking into this, I discovered some correctness issues. Baseline
+        optimizes y=-0.5 by turning it into 1/sqrt(). DFG/FTL optimize constant
+        y=0.5 by turning it into sqrt(). The problem is sqrt() behaves differently
+        for -0 and -Infinity. With sqrt(), negative numbers are undefined,
+        and the result is NaN. With pow(), they have a result.
+
+        Something else that has bothered me for a while is that Math.pow()
+        with the same arguments give you different results in LLINT, Baseline,
+        and DFG/FTL. This seems a bit dangerous for numerical stability.
+
+        With this patch, I unify the behaviors for all tiers while keeping
+        the "special cases".
+
+        We have pow() that is super slow, but most callers don't need the
+        full power. We have:
+        -pow() with an exponent between 0 and 1000 is a fast path implemented
+         by multiplication only.
+        -pow(x, 0.5) is sqrt with special checks for negative values.
+        -pow(x, -0.5) is sqrt with special checks for negative values.
+
+        The C++ implementation handles all those optimizations too. This ensure
+        you get the same results from LLINT to FTL.
+
+        The thunk is eliminated, it was producing incorrect results and only
+        optimized Sunspider's partial-sums.
+
+        DFG gets the optimized integer, 0.5 and -0.5 cases since those are important
+        for somewhat-hot code. DFG falls back to the C++ code for any non-obvious case.
+
+        FTL gets the full C++ implementation inlined in B3. B3 knows how to eliminate
+        all the dead cases so you get the best if your code is hot enough to reach FTL.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode): Deleted.
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToArithSqrt): Deleted.
+        * dfg/DFGNodeType.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::compileArithPowIntegerFastPath):
+        (JSC::DFG::SpeculativeJIT::compileArithPow):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithPow):
+        * jit/ThunkGenerators.cpp:
+        (JSC::powThunkGenerator): Deleted.
+        * jit/ThunkGenerators.h:
+        * runtime/MathCommon.cpp:
+        (JSC::operationMathPow):
+        * runtime/MathCommon.h:
+        * runtime/VM.cpp:
+        (JSC::thunkGeneratorForIntrinsic): Deleted.
+        * tests/stress/math-pow-stable-results.js: Added.
+        Getting consistent results when tiering up is new.
+        This test verify that results always remains the same as LLINT.
+
+        * tests/stress/math-pow-with-constants.js:
+        (testPowUsedAsSqrt):
+        (powUsedAsOneOverSqrt):
+        (testPowUsedAsOneOverSqrt):
+        (powUsedAsSquare):
+        (testPowUsedAsSquare):
+
 2016-04-28  Mark Lam  <mark.lam@apple.com>
 
         DebuggerScope::className() should not assert scope->isValid().
index d848e87..3d5f17f 100644 (file)
@@ -349,7 +349,6 @@ private:
         }
 
         case ArithPow: {
-            node->setResult(NodeResultDouble);
             if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) {
                 fixDoubleOrBooleanEdge(node->child1());
                 fixIntOrBooleanEdge(node->child2());
index 202355b..4b7fa9b 100644 (file)
@@ -633,13 +633,6 @@ struct Node {
         m_op = ToString;
     }
 
-    void convertToArithSqrt()
-    {
-        ASSERT(m_op == ArithPow);
-        child2() = Edge();
-        m_op = ArithSqrt;
-    }
-
     void convertToArithNegate()
     {
         ASSERT(m_op == ArithAbs && child1().useKind() == Int32Use);
index a94468f..c37b8e3 100644 (file)
@@ -153,7 +153,7 @@ namespace JSC { namespace DFG {
     macro(ArithMin, NodeResultNumber) \
     macro(ArithMax, NodeResultNumber) \
     macro(ArithFRound, NodeResultNumber) \
-    macro(ArithPow, NodeResultNumber) \
+    macro(ArithPow, NodeResultDouble) \
     macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
     macro(ArithRound, NodeResultNumber) \
     macro(ArithFloor, NodeResultNumber) \
index 3a21594..67de9a2 100644 (file)
@@ -4722,7 +4722,7 @@ void SpeculativeJIT::compileArithSqrt(Node* node)
 static MacroAssembler::Jump compileArithPowIntegerFastPath(JITCompiler& assembler, FPRReg xOperand, GPRReg yOperand, FPRReg result)
 {
     MacroAssembler::JumpList skipFastPath;
-    skipFastPath.append(assembler.branch32(MacroAssembler::Above, yOperand, MacroAssembler::TrustedImm32(1000)));
+    skipFastPath.append(assembler.branch32(MacroAssembler::Above, yOperand, MacroAssembler::TrustedImm32(maxExponentForIntegerMathPow)));
 
     static const double oneConstant = 1.0;
     assembler.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), result);
@@ -4772,6 +4772,69 @@ void SpeculativeJIT::compileArithPow(Node* node)
         return;
     }
 
+    if (node->child2()->isDoubleConstant()) {
+        double exponent = node->child2()->asNumber();
+        static const double infinityConstant = std::numeric_limits<double>::infinity();
+        static const double minusInfinityConstant = -std::numeric_limits<double>::infinity();
+        if (exponent == 0.5) {
+            SpeculateDoubleOperand xOperand(this, node->child1());
+            FPRTemporary result(this);
+            FPRReg xOperandFpr = xOperand.fpr();
+            FPRReg resultFpr = result.fpr();
+
+            m_jit.moveZeroToDouble(resultFpr);
+            MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
+
+            m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr);
+            MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
+            m_jit.sqrtDouble(xOperandFpr, resultFpr);
+            MacroAssembler::Jump doneWithSqrt = m_jit.jump();
+
+            xIsMinusInfinity.link(&m_jit);
+            if (isX86())
+                m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr);
+            else
+                m_jit.absDouble(resultFpr, resultFpr);
+
+            xIsZeroOrNegativeZero.link(&m_jit);
+            doneWithSqrt.link(&m_jit);
+            doubleResult(resultFpr, node);
+            return;
+        }
+        if (exponent == -0.5) {
+            SpeculateDoubleOperand xOperand(this, node->child1());
+            FPRTemporary scratch(this);
+            FPRTemporary result(this);
+            FPRReg xOperandFpr = xOperand.fpr();
+            FPRReg scratchFPR = scratch.fpr();
+            FPRReg resultFpr = result.fpr();
+
+            m_jit.moveZeroToDouble(resultFpr);
+            MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
+
+            m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr);
+            MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
+
+            static const double oneConstant = 1.;
+            m_jit.loadDouble(TrustedImmPtr(&oneConstant), resultFpr);
+            m_jit.sqrtDouble(xOperandFpr, scratchFPR);
+            m_jit.divDouble(resultFpr, scratchFPR, resultFpr);
+            MacroAssembler::Jump doneWithSqrt = m_jit.jump();
+
+            xIsZeroOrNegativeZero.link(&m_jit);
+            m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr);
+            MacroAssembler::Jump doneWithBaseZero = m_jit.jump();
+
+            xIsMinusInfinity.link(&m_jit);
+            m_jit.moveZeroToDouble(resultFpr);
+
+            doneWithBaseZero.link(&m_jit);
+            doneWithSqrt.link(&m_jit);
+            doubleResult(resultFpr, node);
+            return;
+        }
+    }
+
     SpeculateDoubleOperand xOperand(this, node->child1());
     SpeculateDoubleOperand yOperand(this, node->child2());
     FPRReg xOperandfpr = xOperand.fpr();
index 6d16c37..e69fbe1 100644 (file)
@@ -170,9 +170,10 @@ private:
                 double yOperandValue = m_node->child2()->asNumber();
                 if (yOperandValue == 1) {
                     convertToIdentityOverChild1();
-                } else if (yOperandValue == 0.5) {
-                    m_insertionSet.insertCheck(m_nodeIndex, m_node);
-                    m_node->convertToArithSqrt();
+                    m_changed = true;
+                } else if (yOperandValue == 2) {
+                    m_node->setOp(ArithMul);
+                    m_node->child2() = m_node->child1();
                     m_changed = true;
                 }
             }
index dbbce5b..ebbb76c 100644 (file)
@@ -1859,8 +1859,19 @@ private:
             LBasicBlock integerExponentIsSmallBlock = m_out.newBlock();
             LBasicBlock integerExponentPowBlock = m_out.newBlock();
             LBasicBlock doubleExponentPowBlockEntry = m_out.newBlock();
-            LBasicBlock nanExceptionExponentIsInfinity = m_out.newBlock();
             LBasicBlock nanExceptionBaseIsOne = m_out.newBlock();
+            LBasicBlock nanExceptionExponentIsInfinity = m_out.newBlock();
+            LBasicBlock testExponentIsOneHalf = m_out.newBlock();
+            LBasicBlock handleBaseZeroExponentIsOneHalf = m_out.newBlock();
+            LBasicBlock handleInfinityForExponentIsOneHalf = m_out.newBlock();
+            LBasicBlock exponentIsOneHalfNormal = m_out.newBlock();
+            LBasicBlock exponentIsOneHalfInfinity = m_out.newBlock();
+            LBasicBlock testExponentIsNegativeOneHalf = m_out.newBlock();
+            LBasicBlock testBaseZeroExponentIsNegativeOneHalf = m_out.newBlock();
+            LBasicBlock handleBaseZeroExponentIsNegativeOneHalf = m_out.newBlock();
+            LBasicBlock handleInfinityForExponentIsNegativeOneHalf = m_out.newBlock();
+            LBasicBlock exponentIsNegativeOneHalfNormal = m_out.newBlock();
+            LBasicBlock exponentIsNegativeOneHalfInfinity = m_out.newBlock();
             LBasicBlock powBlock = m_out.newBlock();
             LBasicBlock nanExceptionResultIsNaN = m_out.newBlock();
             LBasicBlock continuation = m_out.newBlock();
@@ -1871,33 +1882,95 @@ private:
             m_out.branch(exponentIsInteger, unsure(integerExponentIsSmallBlock), unsure(doubleExponentPowBlockEntry));
 
             LBasicBlock lastNext = m_out.appendTo(integerExponentIsSmallBlock, integerExponentPowBlock);
-            LValue integerExponentBelow1000 = m_out.below(integerExponent, m_out.constInt32(1000));
-            m_out.branch(integerExponentBelow1000, usually(integerExponentPowBlock), rarely(doubleExponentPowBlockEntry));
+            LValue integerExponentBelowMax = m_out.belowOrEqual(integerExponent, m_out.constInt32(maxExponentForIntegerMathPow));
+            m_out.branch(integerExponentBelowMax, usually(integerExponentPowBlock), rarely(doubleExponentPowBlockEntry));
 
             m_out.appendTo(integerExponentPowBlock, doubleExponentPowBlockEntry);
             ValueFromBlock powDoubleIntResult = m_out.anchor(m_out.doublePowi(base, integerExponent));
             m_out.jump(continuation);
 
             // If y is NaN, the result is NaN.
-            m_out.appendTo(doubleExponentPowBlockEntry, nanExceptionExponentIsInfinity);
+            m_out.appendTo(doubleExponentPowBlockEntry, nanExceptionBaseIsOne);
             LValue exponentIsNaN;
             if (provenType(m_node->child2()) & SpecDoubleNaN)
                 exponentIsNaN = m_out.doubleNotEqualOrUnordered(exponent, exponent);
             else
                 exponentIsNaN = m_out.booleanFalse;
-            m_out.branch(exponentIsNaN, rarely(nanExceptionResultIsNaN), usually(nanExceptionExponentIsInfinity));
+            m_out.branch(exponentIsNaN, rarely(nanExceptionResultIsNaN), usually(nanExceptionBaseIsOne));
 
             // If abs(x) is 1 and y is +infinity, the result is NaN.
             // If abs(x) is 1 and y is -infinity, the result is NaN.
-            m_out.appendTo(nanExceptionExponentIsInfinity, nanExceptionBaseIsOne);
-            LValue absoluteExponent = m_out.doubleAbs(exponent);
-            LValue absoluteExponentIsInfinity = m_out.doubleEqual(absoluteExponent, m_out.constDouble(std::numeric_limits<double>::infinity()));
-            m_out.branch(absoluteExponentIsInfinity, rarely(nanExceptionBaseIsOne), usually(powBlock));
 
-            m_out.appendTo(nanExceptionBaseIsOne, powBlock);
+            //     Test if base == 1.
+            m_out.appendTo(nanExceptionBaseIsOne, nanExceptionExponentIsInfinity);
             LValue absoluteBase = m_out.doubleAbs(base);
             LValue absoluteBaseIsOne = m_out.doubleEqual(absoluteBase, m_out.constDouble(1));
-            m_out.branch(absoluteBaseIsOne, unsure(nanExceptionResultIsNaN), unsure(powBlock));
+            m_out.branch(absoluteBaseIsOne, rarely(nanExceptionExponentIsInfinity), usually(testExponentIsOneHalf));
+
+            //     Test if abs(y) == Infinity.
+            m_out.appendTo(nanExceptionExponentIsInfinity, testExponentIsOneHalf);
+            LValue absoluteExponent = m_out.doubleAbs(exponent);
+            LValue absoluteExponentIsInfinity = m_out.doubleEqual(absoluteExponent, m_out.constDouble(std::numeric_limits<double>::infinity()));
+            m_out.branch(absoluteExponentIsInfinity, rarely(nanExceptionResultIsNaN), usually(testExponentIsOneHalf));
+
+            // If y == 0.5 or y == -0.5, handle it through SQRT.
+            // We have be carefuly with -0 and -Infinity.
+
+            //     Test if y == 0.5
+            m_out.appendTo(testExponentIsOneHalf, handleBaseZeroExponentIsOneHalf);
+            LValue exponentIsOneHalf = m_out.doubleEqual(exponent, m_out.constDouble(0.5));
+            m_out.branch(exponentIsOneHalf, rarely(handleBaseZeroExponentIsOneHalf), usually(testExponentIsNegativeOneHalf));
+
+            //     Handle x == -0.
+            m_out.appendTo(handleBaseZeroExponentIsOneHalf, handleInfinityForExponentIsOneHalf);
+            LValue baseIsZeroExponentIsOneHalf = m_out.doubleEqual(base, m_out.doubleZero);
+            ValueFromBlock zeroResultExponentIsOneHalf = m_out.anchor(m_out.doubleZero);
+            m_out.branch(baseIsZeroExponentIsOneHalf, rarely(continuation), usually(handleInfinityForExponentIsOneHalf));
+
+            //     Test if abs(x) == Infinity.
+            m_out.appendTo(handleInfinityForExponentIsOneHalf, exponentIsOneHalfNormal);
+            LValue absoluteBaseIsInfinityOneHalf = m_out.doubleEqual(absoluteBase, m_out.constDouble(std::numeric_limits<double>::infinity()));
+            m_out.branch(absoluteBaseIsInfinityOneHalf, rarely(exponentIsOneHalfInfinity), usually(exponentIsOneHalfNormal));
+
+            //     The exponent is 0.5, the base is finite or NaN, we can use SQRT.
+            m_out.appendTo(exponentIsOneHalfNormal, exponentIsOneHalfInfinity);
+            ValueFromBlock sqrtResult = m_out.anchor(m_out.doubleSqrt(base));
+            m_out.jump(continuation);
+
+            //     The exponent is 0.5, the base is infinite, the result is always infinite.
+            m_out.appendTo(exponentIsOneHalfInfinity, testExponentIsNegativeOneHalf);
+            ValueFromBlock sqrtInfinityResult = m_out.anchor(m_out.constDouble(std::numeric_limits<double>::infinity()));
+            m_out.jump(continuation);
+
+            //     Test if y == -0.5
+            m_out.appendTo(testExponentIsNegativeOneHalf, testBaseZeroExponentIsNegativeOneHalf);
+            LValue exponentIsNegativeOneHalf = m_out.doubleEqual(exponent, m_out.constDouble(-0.5));
+            m_out.branch(exponentIsNegativeOneHalf, rarely(testBaseZeroExponentIsNegativeOneHalf), usually(powBlock));
+
+            //     Handle x == -0.
+            m_out.appendTo(testBaseZeroExponentIsNegativeOneHalf, handleBaseZeroExponentIsNegativeOneHalf);
+            LValue baseIsZeroExponentIsNegativeOneHalf = m_out.doubleEqual(base, m_out.doubleZero);
+            m_out.branch(baseIsZeroExponentIsNegativeOneHalf, rarely(handleBaseZeroExponentIsNegativeOneHalf), usually(handleInfinityForExponentIsNegativeOneHalf));
+
+            m_out.appendTo(handleBaseZeroExponentIsNegativeOneHalf, handleInfinityForExponentIsNegativeOneHalf);
+            ValueFromBlock oneOverSqrtZeroResult = m_out.anchor(m_out.constDouble(std::numeric_limits<double>::infinity()));
+            m_out.jump(continuation);
+
+            //     Test if abs(x) == Infinity.
+            m_out.appendTo(handleInfinityForExponentIsNegativeOneHalf, exponentIsNegativeOneHalfNormal);
+            LValue absoluteBaseIsInfinityNegativeOneHalf = m_out.doubleEqual(absoluteBase, m_out.constDouble(std::numeric_limits<double>::infinity()));
+            m_out.branch(absoluteBaseIsInfinityNegativeOneHalf, rarely(exponentIsNegativeOneHalfInfinity), usually(exponentIsNegativeOneHalfNormal));
+
+            //     The exponent is -0.5, the base is finite or NaN, we can use 1/SQRT.
+            m_out.appendTo(exponentIsNegativeOneHalfNormal, exponentIsNegativeOneHalfInfinity);
+            LValue sqrtBase = m_out.doubleSqrt(base);
+            ValueFromBlock oneOverSqrtResult = m_out.anchor(m_out.div(m_out.constDouble(1.), sqrtBase));
+            m_out.jump(continuation);
+
+            //     The exponent is -0.5, the base is infinite, the result is always zero.
+            m_out.appendTo(exponentIsNegativeOneHalfInfinity, powBlock);
+            ValueFromBlock oneOverSqrtInfinityResult = m_out.anchor(m_out.doubleZero);
+            m_out.jump(continuation);
 
             m_out.appendTo(powBlock, nanExceptionResultIsNaN);
             ValueFromBlock powResult = m_out.anchor(m_out.doublePow(base, exponent));
@@ -1908,7 +1981,7 @@ private:
             m_out.jump(continuation);
 
             m_out.appendTo(continuation, lastNext);
-            setDouble(m_out.phi(m_out.doubleType, powDoubleIntResult, powResult, pureNan));
+            setDouble(m_out.phi(m_out.doubleType, powDoubleIntResult, zeroResultExponentIsOneHalf, sqrtResult, sqrtInfinityResult, oneOverSqrtZeroResult, oneOverSqrtResult, oneOverSqrtInfinityResult, powResult, pureNan));
         }
     }
 
index 9bf9968..8f94004 100644 (file)
@@ -782,8 +782,6 @@ defineUnaryDoubleOpWrapper(floor);
 defineUnaryDoubleOpWrapper(ceil);
 defineUnaryDoubleOpWrapper(trunc);
 
-static const double oneConstant = 1.0;
-static const double negativeHalfConstant = -0.5;
 static const double halfConstant = 0.5;
     
 MacroAssemblerCodeRef floorThunkGenerator(VM* vm)
@@ -992,58 +990,6 @@ MacroAssemblerCodeRef absThunkGenerator(VM* vm)
     return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "abs");
 }
 
-MacroAssemblerCodeRef powThunkGenerator(VM* vm)
-{
-    SpecializedThunkJIT jit(vm, 2);
-    if (!jit.supportsFloatingPoint())
-        return MacroAssemblerCodeRef::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm));
-
-    jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), SpecializedThunkJIT::fpRegT1);
-    jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0);
-    MacroAssembler::Jump nonIntExponent;
-    jit.loadInt32Argument(1, SpecializedThunkJIT::regT0, nonIntExponent);
-    jit.appendFailure(jit.branch32(MacroAssembler::LessThan, SpecializedThunkJIT::regT0, MacroAssembler::TrustedImm32(0)));
-    
-    MacroAssembler::Jump exponentIsZero = jit.branchTest32(MacroAssembler::Zero, SpecializedThunkJIT::regT0);
-    MacroAssembler::Label startLoop(jit.label());
-
-    MacroAssembler::Jump exponentIsEven = jit.branchTest32(MacroAssembler::Zero, SpecializedThunkJIT::regT0, MacroAssembler::TrustedImm32(1));
-    jit.mulDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1);
-    exponentIsEven.link(&jit);
-    jit.mulDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0);
-    jit.rshift32(MacroAssembler::TrustedImm32(1), SpecializedThunkJIT::regT0);
-    jit.branchTest32(MacroAssembler::NonZero, SpecializedThunkJIT::regT0).linkTo(startLoop, &jit);
-
-    exponentIsZero.link(&jit);
-
-    {
-        SpecializedThunkJIT::JumpList doubleResult;
-        jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT1, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT0);
-        jit.returnInt32(SpecializedThunkJIT::regT0);
-        doubleResult.link(&jit);
-        jit.returnDouble(SpecializedThunkJIT::fpRegT1);
-    }
-
-    if (jit.supportsFloatingPointSqrt()) {
-        nonIntExponent.link(&jit);
-        jit.loadDouble(MacroAssembler::TrustedImmPtr(&negativeHalfConstant), SpecializedThunkJIT::fpRegT3);
-        jit.loadDoubleArgument(1, SpecializedThunkJIT::fpRegT2, SpecializedThunkJIT::regT0);
-        jit.appendFailure(jit.branchDouble(MacroAssembler::DoubleLessThanOrEqual, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1));
-        jit.appendFailure(jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, SpecializedThunkJIT::fpRegT2, SpecializedThunkJIT::fpRegT3));
-        jit.sqrtDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0);
-        jit.divDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1);
-
-        SpecializedThunkJIT::JumpList doubleResult;
-        jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT1, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT0);
-        jit.returnInt32(SpecializedThunkJIT::regT0);
-        doubleResult.link(&jit);
-        jit.returnDouble(SpecializedThunkJIT::fpRegT1);
-    } else
-        jit.appendFailure(nonIntExponent);
-
-    return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "pow");
-}
-
 MacroAssemblerCodeRef imulThunkGenerator(VM* vm)
 {
     SpecializedThunkJIT jit(vm, 2);
index e768c51..6388727 100644 (file)
@@ -61,7 +61,6 @@ MacroAssemblerCodeRef floorThunkGenerator(VM*);
 MacroAssemblerCodeRef logThunkGenerator(VM*);
 MacroAssemblerCodeRef roundThunkGenerator(VM*);
 MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
-MacroAssemblerCodeRef powThunkGenerator(VM*);
 MacroAssemblerCodeRef imulThunkGenerator(VM*);
 MacroAssemblerCodeRef randomThunkGenerator(VM*);
 MacroAssemblerCodeRef truncThunkGenerator(VM*);
index aec296f..6ede6ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -414,21 +414,40 @@ double JIT_OPERATION operationMathPow(double x, double y)
 {
     if (std::isnan(y))
         return PNaN;
-    if (std::isinf(y) && fabs(x) == 1)
+    double absoluteBase = fabs(x);
+    if (absoluteBase == 1 && std::isinf(y))
         return PNaN;
+
+    if (y == 0.5) {
+        if (!absoluteBase)
+            return 0;
+        if (absoluteBase == std::numeric_limits<double>::infinity())
+            return std::numeric_limits<double>::infinity();
+        return sqrt(x);
+    }
+
+    if (y == -0.5) {
+        if (!absoluteBase)
+            return std::numeric_limits<double>::infinity();
+        if (absoluteBase == std::numeric_limits<double>::infinity())
+            return 0.;
+        return 1. / sqrt(x);
+    }
+
     int32_t yAsInt = y;
-    if (static_cast<double>(yAsInt) != y || yAsInt < 0)
-        return mathPowInternal(x, y);
-
-    // If the exponent is a positive int32 integer, we do a fast exponentiation
-    double result = 1;
-    while (yAsInt) {
-        if (yAsInt & 1)
-            result *= x;
-        x *= x;
-        yAsInt >>= 1;
+    if (static_cast<double>(yAsInt) == y && yAsInt > 0 && yAsInt <= maxExponentForIntegerMathPow) {
+        // If the exponent is a small positive int32 integer, we do a fast exponentiation
+        double result = 1;
+        while (yAsInt) {
+            if (yAsInt & 1)
+                result *= x;
+            x *= x;
+            yAsInt >>= 1;
+        }
+        return result;
+
     }
-    return result;
+    return mathPowInternal(x, y);
 }
 
 extern "C" {
index f62e8a4..d29a510 100644 (file)
@@ -35,6 +35,8 @@
 #endif
 
 namespace JSC {
+
+const int32_t maxExponentForIntegerMathPow = 1000;
 double JIT_OPERATION operationMathPow(double x, double y) WTF_INTERNAL;
 
 inline int clz32(uint32_t number)
index b6e954e..cb6bb39 100644 (file)
@@ -474,8 +474,6 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
         return fromCharCodeThunkGenerator;
     case SqrtIntrinsic:
         return sqrtThunkGenerator;
-    case PowIntrinsic:
-        return powThunkGenerator;
     case AbsIntrinsic:
         return absThunkGenerator;
     case FloorIntrinsic:
diff --git a/Source/JavaScriptCore/tests/stress/math-pow-stable-results.js b/Source/JavaScriptCore/tests/stress/math-pow-stable-results.js
new file mode 100644 (file)
index 0000000..ea6e62b
--- /dev/null
@@ -0,0 +1,85 @@
+// This test verify the results of Math.pow() do not change as we change optimization tier.
+
+function opaquePow(a, b)
+{
+    return Math.pow(a, b);
+}
+noInline(opaquePow);
+
+
+let caseStrings = [
+    "0",
+    "-0.",
+    "0.5",
+    "1",
+    "2",
+    "-0.5",
+    "-1",
+    "999",
+    "1000",
+    "1001",
+    "NaN",
+    "Infinity",
+    "-Infinity",
+    "Math.PI",
+    "Math.LN2",
+    "Math.LN10",
+    "Math.E",
+    "Math.LOG10E",
+    "Math.LOG2E",
+    "Math.SQRT2"
+];
+let cases = [];
+for (let caseString of caseStrings) {
+    cases.push(eval(caseString));
+}
+
+let expectedResults = [];
+let constantBaseFunctions = [];
+let constantExponentFunctions = [];
+for (let i = 0; i < cases.length; ++i) {
+    let base = cases[i];
+
+    expectedResults[i] = [];
+    for (let j = 0; j < cases.length; ++j) {
+        let exponent = cases[j];
+        expectedResults[i][j] = Math.pow(base, exponent);
+    }
+
+    eval("constantBaseFunctions[i] = function (exponent) { return Math.pow(" + caseStrings[i] + ", exponent); }");
+    eval("constantExponentFunctions[i] = function (base) { return Math.pow(base, " + caseStrings[i] + "); }");
+}
+
+function isIdentical(result, expected)
+{
+    if (expected === expected) {
+        if (result !== expected)
+            return false;
+        if (!expected && 1 / expected === -Infinity && 1 / result !== -Infinity)
+            return false;
+
+        return true;
+    }
+    return result !== result;
+}
+
+for (let tierUpLoopCounter = 0; tierUpLoopCounter < 1e3; ++tierUpLoopCounter) {
+    for (let i = 0; i < cases.length; ++i) {
+        let base = cases[i];
+        for (let j = 0; j < cases.length; ++j) {
+            let exponent = cases[j];
+            let expectedResult = expectedResults[i][j];
+            let result = opaquePow(base, exponent);
+            if (!isIdentical(result, expectedResult))
+                throw "Failed opaquePow with base = " + base + " exponent = " + exponent;
+
+            result = constantBaseFunctions[i](exponent);
+            if (!isIdentical(result, expectedResult))
+                throw "Failed constantBaseFunctions with base = " + base + " exponent = " + exponent;
+
+            result = constantExponentFunctions[j](base);
+            if (!isIdentical(result, expectedResult))
+                throw "Failed constantExponentFunctions with base = " + base + " exponent = " + exponent;
+        }
+    }
+}
index d25902d..090f5b7 100644 (file)
@@ -44,20 +44,90 @@ function powUsedAsSqrt(x) {
 noInline(powUsedAsSqrt);
 
 function testPowUsedAsSqrt() {
-    for (var i = 0; i < 10000; ++i) {
-        var result = powUsedAsSqrt(4);
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsSqrt(4);
         if (result !== Math.sqrt(4))
             throw "Error: powUsedAsSqrt(4) should be 2, was = " + result;
-    }
-    for (var i = 0; i < 10000; ++i) {
-        var result = powUsedAsSqrt(4.4);
+        result = powUsedAsSqrt(4.4);
         if (result !== Math.sqrt(4.4))
             throw "Error: powUsedAsSqrt(4) should be " + Math.sqrt(4.4) + ", was = " + result;
+        if (powUsedAsSqrt(Infinity) !== Infinity)
+            throw "Failed powUsedAsSqrt(Infinity)";
+        if (powUsedAsSqrt(-Infinity) !== Infinity)
+            throw "Failed powUsedAsSqrt(-Infinity)";
+        let nanResult = powUsedAsSqrt(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsSqrt(NaN)";
+        let zeroResult = powUsedAsSqrt(0.)
+        if (zeroResult || (1 / zeroResult) !== Infinity)
+            throw "Failed powUsedAsSqrt(0.)";
+        let negativeZeroResult = powUsedAsSqrt(-0)
+        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
+            throw "Failed powUsedAsSqrt(-0)";
     }
-
 }
 testPowUsedAsSqrt();
 
+function powUsedAsOneOverSqrt(x) {
+    return Math.pow(x, -0.5);
+}
+noInline(powUsedAsOneOverSqrt);
+
+function testPowUsedAsOneOverSqrt() {
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsOneOverSqrt(4);
+        if (result !== 0.5)
+            throw "Error: powUsedAsOneOverSqrt(4) should be 0.5, was = " + result;
+        result = powUsedAsOneOverSqrt(4.4);
+        if (result !== 1/Math.sqrt(4.4))
+            throw "Error: powUsedAsOneOverSqrt(4) should be " + 1/Math.sqrt(4.4) + ", was = " + result;
+        if (powUsedAsOneOverSqrt(Infinity) !== 0)
+            throw "Failed powUsedAsOneOverSqrt(Infinity)";
+        if (powUsedAsOneOverSqrt(-Infinity) !== 0)
+            throw "Failed powUsedAsOneOverSqrt(-Infinity)";
+        let nanResult = powUsedAsOneOverSqrt(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsOneOverSqrt(NaN)";
+        if (powUsedAsOneOverSqrt(0) !== Infinity)
+            throw "Failed powUsedAsOneOverSqrt(0)";
+        if (powUsedAsOneOverSqrt(-0.) !== Infinity)
+            throw "Failed powUsedAsOneOverSqrt(-0.)";
+    }
+}
+testPowUsedAsOneOverSqrt();
+
+function powUsedAsSquare(x) {
+    return Math.pow(x, 2);
+}
+noInline(powUsedAsSquare);
+
+function testPowUsedAsSquare() {
+    for (let i = 0; i < 1e4; ++i) {
+        let result = powUsedAsSquare(2);
+        if (result !== 4)
+            throw "Error: powUsedAsSquare(4) should be 2, was = " + result;
+        result = powUsedAsSquare(4.4);
+        if (result !== 19.360000000000003)
+            throw "Error: powUsedAsSquare(4) should be " + 19.360000000000003 + ", was = " + result;
+        result = powUsedAsSquare(Math.PI);
+        if (result !== 9.869604401089358)
+            throw "Error: powUsedAsSquare(4) should be " + 9.869604401089358 + ", was = " + result;
+        if (powUsedAsSquare(Infinity) !== Infinity)
+            throw "Failed powUsedAsSquare(Infinity)";
+        if (powUsedAsSquare(-Infinity) !== Infinity)
+            throw "Failed powUsedAsSquare(-Infinity)";
+        let nanResult = powUsedAsSquare(NaN)
+        if (nanResult === nanResult)
+            throw "Failed powUsedAsSquare(NaN)";
+        let zeroResult = powUsedAsSquare(0.)
+        if (zeroResult || (1 / zeroResult) !== Infinity)
+            throw "Failed powUsedAsSquare(0.)";
+        let negativeZeroResult = powUsedAsSquare(-0)
+        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
+            throw "Failed powUsedAsSquare(-0)";
+    }
+}
+testPowUsedAsSquare();
 
 function intIntConstantsSmallNumbers() {
     return Math.pow(42, 3);