Title: [200208] trunk/Source/_javascript_Core
Revision
200208
Author
benja...@webkit.org
Date
2016-04-28 13:50:08 -0700 (Thu, 28 Apr 2016)

Log Message

[JSC] Unify Math.pow() accross all tiers
https://bugs.webkit.org/show_bug.cgi?id=157121

Patch by Benjamin Poulain <bpoul...@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):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (200207 => 200208)


--- trunk/Source/_javascript_Core/ChangeLog	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-04-28 20:50:08 UTC (rev 200208)
@@ -1,3 +1,82 @@
+2016-04-28  Benjamin Poulain  <bpoul...@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....@apple.com>
 
         DebuggerScope::className() should not assert scope->isValid().

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -349,7 +349,6 @@
         }
 
         case ArithPow: {
-            node->setResult(NodeResultDouble);
             if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) {
                 fixDoubleOrBooleanEdge(node->child1());
                 fixIntOrBooleanEdge(node->child2());

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (200207 => 200208)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-04-28 20:50:08 UTC (rev 200208)
@@ -633,13 +633,6 @@
         m_op = ToString;
     }
 
-    void convertToArithSqrt()
-    {
-        ASSERT(m_op == ArithPow);
-        child2() = Edge();
-        m_op = ArithSqrt;
-    }
-
     void convertToArithNegate()
     {
         ASSERT(m_op == ArithAbs && child1().useKind() == Int32Use);

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (200207 => 200208)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-04-28 20:50:08 UTC (rev 200208)
@@ -153,7 +153,7 @@
     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) \

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -4722,7 +4722,7 @@
 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 @@
         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();

Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -170,10 +170,11 @@
                 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;
                 }
             }
             break;

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -1859,8 +1859,19 @@
             LBasicBlock integerExponentIsSmallBlock = m_out.newBlock();
             LBasicBlock integerExponentPowBlock = m_out.newBlock();
             LBasicBlock doubleExponentPowBlockEntry = m_out.newBlock();
+            LBasicBlock nanExceptionBaseIsOne = m_out.newBlock();
             LBasicBlock nanExceptionExponentIsInfinity = m_out.newBlock();
-            LBasicBlock nanExceptionBaseIsOne = 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,34 +1882,96 @@
             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));
             m_out.jump(continuation);
@@ -1908,7 +1981,7 @@
             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));
         }
     }
 

Modified: trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -782,8 +782,6 @@
 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 @@
     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);

Modified: trunk/Source/_javascript_Core/jit/ThunkGenerators.h (200207 => 200208)


--- trunk/Source/_javascript_Core/jit/ThunkGenerators.h	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/jit/ThunkGenerators.h	2016-04-28 20:50:08 UTC (rev 200208)
@@ -61,7 +61,6 @@
 MacroAssemblerCodeRef logThunkGenerator(VM*);
 MacroAssemblerCodeRef roundThunkGenerator(VM*);
 MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
-MacroAssemblerCodeRef powThunkGenerator(VM*);
 MacroAssemblerCodeRef imulThunkGenerator(VM*);
 MacroAssemblerCodeRef randomThunkGenerator(VM*);
 MacroAssemblerCodeRef truncThunkGenerator(VM*);

Modified: trunk/Source/_javascript_Core/runtime/MathCommon.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/runtime/MathCommon.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/runtime/MathCommon.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -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 @@
 {
     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 (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;
 
-    // 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;
     }
-    return result;
+    return mathPowInternal(x, y);
 }
 
 extern "C" {

Modified: trunk/Source/_javascript_Core/runtime/MathCommon.h (200207 => 200208)


--- trunk/Source/_javascript_Core/runtime/MathCommon.h	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/runtime/MathCommon.h	2016-04-28 20:50:08 UTC (rev 200208)
@@ -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)

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (200207 => 200208)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2016-04-28 20:50:08 UTC (rev 200208)
@@ -474,8 +474,6 @@
         return fromCharCodeThunkGenerator;
     case SqrtIntrinsic:
         return sqrtThunkGenerator;
-    case PowIntrinsic:
-        return powThunkGenerator;
     case AbsIntrinsic:
         return absThunkGenerator;
     case FloorIntrinsic:

Added: trunk/Source/_javascript_Core/tests/stress/math-pow-stable-results.js (0 => 200208)


--- trunk/Source/_javascript_Core/tests/stress/math-pow-stable-results.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/math-pow-stable-results.js	2016-04-28 20:50:08 UTC (rev 200208)
@@ -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;
+        }
+    }
+}

Modified: trunk/Source/_javascript_Core/tests/stress/math-pow-with-constants.js (200207 => 200208)


--- trunk/Source/_javascript_Core/tests/stress/math-pow-with-constants.js	2016-04-28 20:31:12 UTC (rev 200207)
+++ trunk/Source/_javascript_Core/tests/stress/math-pow-with-constants.js	2016-04-28 20:50:08 UTC (rev 200208)
@@ -44,21 +44,91 @@
 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);
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to