Changeset 200208 in webkit


Ignore:
Timestamp:
Apr 28, 2016 1:50:08 PM (8 years ago)
Author:
benjamin@webkit.org
Message:

[JSC] Unify Math.pow() accross all tiers
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):

Location:
trunk/Source/JavaScriptCore
Files:
1 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r200200 r200208  
     12016-04-28  Benjamin Poulain  <bpoulain@apple.com>
     2
     3        [JSC] Unify Math.pow() accross all tiers
     4        https://bugs.webkit.org/show_bug.cgi?id=157121
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        My previous optimizations of DFG compile time have slowly
     9        regressed Sunspider's math-partial-sums.
     10
     11        What is happenning is baseline used a thunk for Math.pow()
     12        that has a special case for an exponent of -0.5, while
     13        DFG/FTL have other special cases for other exponents.
     14        The faster we get to DFG, the less time we spend in that fast
     15        case for -0.5.
     16
     17        While looking into this, I discovered some correctness issues. Baseline
     18        optimizes y=-0.5 by turning it into 1/sqrt(). DFG/FTL optimize constant
     19        y=0.5 by turning it into sqrt(). The problem is sqrt() behaves differently
     20        for -0 and -Infinity. With sqrt(), negative numbers are undefined,
     21        and the result is NaN. With pow(), they have a result.
     22
     23        Something else that has bothered me for a while is that Math.pow()
     24        with the same arguments give you different results in LLINT, Baseline,
     25        and DFG/FTL. This seems a bit dangerous for numerical stability.
     26
     27        With this patch, I unify the behaviors for all tiers while keeping
     28        the "special cases".
     29
     30        We have pow() that is super slow, but most callers don't need the
     31        full power. We have:
     32        -pow() with an exponent between 0 and 1000 is a fast path implemented
     33         by multiplication only.
     34        -pow(x, 0.5) is sqrt with special checks for negative values.
     35        -pow(x, -0.5) is sqrt with special checks for negative values.
     36
     37        The C++ implementation handles all those optimizations too. This ensure
     38        you get the same results from LLINT to FTL.
     39
     40        The thunk is eliminated, it was producing incorrect results and only
     41        optimized Sunspider's partial-sums.
     42
     43        DFG gets the optimized integer, 0.5 and -0.5 cases since those are important
     44        for somewhat-hot code. DFG falls back to the C++ code for any non-obvious case.
     45
     46        FTL gets the full C++ implementation inlined in B3. B3 knows how to eliminate
     47        all the dead cases so you get the best if your code is hot enough to reach FTL.
     48
     49        * dfg/DFGFixupPhase.cpp:
     50        (JSC::DFG::FixupPhase::fixupNode): Deleted.
     51        * dfg/DFGNode.h:
     52        (JSC::DFG::Node::convertToArithSqrt): Deleted.
     53        * dfg/DFGNodeType.h:
     54        * dfg/DFGSpeculativeJIT.cpp:
     55        (JSC::DFG::compileArithPowIntegerFastPath):
     56        (JSC::DFG::SpeculativeJIT::compileArithPow):
     57        * dfg/DFGStrengthReductionPhase.cpp:
     58        (JSC::DFG::StrengthReductionPhase::handleNode):
     59        * ftl/FTLLowerDFGToB3.cpp:
     60        (JSC::FTL::DFG::LowerDFGToB3::compileArithPow):
     61        * jit/ThunkGenerators.cpp:
     62        (JSC::powThunkGenerator): Deleted.
     63        * jit/ThunkGenerators.h:
     64        * runtime/MathCommon.cpp:
     65        (JSC::operationMathPow):
     66        * runtime/MathCommon.h:
     67        * runtime/VM.cpp:
     68        (JSC::thunkGeneratorForIntrinsic): Deleted.
     69        * tests/stress/math-pow-stable-results.js: Added.
     70        Getting consistent results when tiering up is new.
     71        This test verify that results always remains the same as LLINT.
     72
     73        * tests/stress/math-pow-with-constants.js:
     74        (testPowUsedAsSqrt):
     75        (powUsedAsOneOverSqrt):
     76        (testPowUsedAsOneOverSqrt):
     77        (powUsedAsSquare):
     78        (testPowUsedAsSquare):
     79
    1802016-04-28  Mark Lam  <mark.lam@apple.com>
    281
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r200149 r200208  
    350350
    351351        case ArithPow: {
    352             node->setResult(NodeResultDouble);
    353352            if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) {
    354353                fixDoubleOrBooleanEdge(node->child1());
  • trunk/Source/JavaScriptCore/dfg/DFGNode.h

    r200117 r200208  
    632632        ASSERT(m_op == ToPrimitive);
    633633        m_op = ToString;
    634     }
    635 
    636     void convertToArithSqrt()
    637     {
    638         ASSERT(m_op == ArithPow);
    639         child2() = Edge();
    640         m_op = ArithSqrt;
    641634    }
    642635
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r200149 r200208  
    154154    macro(ArithMax, NodeResultNumber) \
    155155    macro(ArithFRound, NodeResultNumber) \
    156     macro(ArithPow, NodeResultNumber) \
     156    macro(ArithPow, NodeResultDouble) \
    157157    macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
    158158    macro(ArithRound, NodeResultNumber) \
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r200149 r200208  
    47234723{
    47244724    MacroAssembler::JumpList skipFastPath;
    4725     skipFastPath.append(assembler.branch32(MacroAssembler::Above, yOperand, MacroAssembler::TrustedImm32(1000)));
     4725    skipFastPath.append(assembler.branch32(MacroAssembler::Above, yOperand, MacroAssembler::TrustedImm32(maxExponentForIntegerMathPow)));
    47264726
    47274727    static const double oneConstant = 1.0;
     
    47714771        doubleResult(resultFpr, node);
    47724772        return;
     4773    }
     4774
     4775    if (node->child2()->isDoubleConstant()) {
     4776        double exponent = node->child2()->asNumber();
     4777        static const double infinityConstant = std::numeric_limits<double>::infinity();
     4778        static const double minusInfinityConstant = -std::numeric_limits<double>::infinity();
     4779        if (exponent == 0.5) {
     4780            SpeculateDoubleOperand xOperand(this, node->child1());
     4781            FPRTemporary result(this);
     4782            FPRReg xOperandFpr = xOperand.fpr();
     4783            FPRReg resultFpr = result.fpr();
     4784
     4785            m_jit.moveZeroToDouble(resultFpr);
     4786            MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
     4787
     4788            m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr);
     4789            MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
     4790            m_jit.sqrtDouble(xOperandFpr, resultFpr);
     4791            MacroAssembler::Jump doneWithSqrt = m_jit.jump();
     4792
     4793            xIsMinusInfinity.link(&m_jit);
     4794            if (isX86())
     4795                m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr);
     4796            else
     4797                m_jit.absDouble(resultFpr, resultFpr);
     4798
     4799            xIsZeroOrNegativeZero.link(&m_jit);
     4800            doneWithSqrt.link(&m_jit);
     4801            doubleResult(resultFpr, node);
     4802            return;
     4803        }
     4804        if (exponent == -0.5) {
     4805            SpeculateDoubleOperand xOperand(this, node->child1());
     4806            FPRTemporary scratch(this);
     4807            FPRTemporary result(this);
     4808            FPRReg xOperandFpr = xOperand.fpr();
     4809            FPRReg scratchFPR = scratch.fpr();
     4810            FPRReg resultFpr = result.fpr();
     4811
     4812            m_jit.moveZeroToDouble(resultFpr);
     4813            MacroAssembler::Jump xIsZeroOrNegativeZero = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
     4814
     4815            m_jit.loadDouble(TrustedImmPtr(&minusInfinityConstant), resultFpr);
     4816            MacroAssembler::Jump xIsMinusInfinity = m_jit.branchDouble(MacroAssembler::DoubleEqual, xOperandFpr, resultFpr);
     4817
     4818            static const double oneConstant = 1.;
     4819            m_jit.loadDouble(TrustedImmPtr(&oneConstant), resultFpr);
     4820            m_jit.sqrtDouble(xOperandFpr, scratchFPR);
     4821            m_jit.divDouble(resultFpr, scratchFPR, resultFpr);
     4822            MacroAssembler::Jump doneWithSqrt = m_jit.jump();
     4823
     4824            xIsZeroOrNegativeZero.link(&m_jit);
     4825            m_jit.loadDouble(TrustedImmPtr(&infinityConstant), resultFpr);
     4826            MacroAssembler::Jump doneWithBaseZero = m_jit.jump();
     4827
     4828            xIsMinusInfinity.link(&m_jit);
     4829            m_jit.moveZeroToDouble(resultFpr);
     4830
     4831            doneWithBaseZero.link(&m_jit);
     4832            doneWithSqrt.link(&m_jit);
     4833            doubleResult(resultFpr, node);
     4834            return;
     4835        }
    47734836    }
    47744837
  • trunk/Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp

    r200117 r200208  
    171171                if (yOperandValue == 1) {
    172172                    convertToIdentityOverChild1();
    173                 } else if (yOperandValue == 0.5) {
    174                     m_insertionSet.insertCheck(m_nodeIndex, m_node);
    175                     m_node->convertToArithSqrt();
     173                    m_changed = true;
     174                } else if (yOperandValue == 2) {
     175                    m_node->setOp(ArithMul);
     176                    m_node->child2() = m_node->child1();
    176177                    m_changed = true;
    177178                }
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r200149 r200208  
    18601860            LBasicBlock integerExponentPowBlock = m_out.newBlock();
    18611861            LBasicBlock doubleExponentPowBlockEntry = m_out.newBlock();
     1862            LBasicBlock nanExceptionBaseIsOne = m_out.newBlock();
    18621863            LBasicBlock nanExceptionExponentIsInfinity = m_out.newBlock();
    1863             LBasicBlock nanExceptionBaseIsOne = m_out.newBlock();
     1864            LBasicBlock testExponentIsOneHalf = m_out.newBlock();
     1865            LBasicBlock handleBaseZeroExponentIsOneHalf = m_out.newBlock();
     1866            LBasicBlock handleInfinityForExponentIsOneHalf = m_out.newBlock();
     1867            LBasicBlock exponentIsOneHalfNormal = m_out.newBlock();
     1868            LBasicBlock exponentIsOneHalfInfinity = m_out.newBlock();
     1869            LBasicBlock testExponentIsNegativeOneHalf = m_out.newBlock();
     1870            LBasicBlock testBaseZeroExponentIsNegativeOneHalf = m_out.newBlock();
     1871            LBasicBlock handleBaseZeroExponentIsNegativeOneHalf = m_out.newBlock();
     1872            LBasicBlock handleInfinityForExponentIsNegativeOneHalf = m_out.newBlock();
     1873            LBasicBlock exponentIsNegativeOneHalfNormal = m_out.newBlock();
     1874            LBasicBlock exponentIsNegativeOneHalfInfinity = m_out.newBlock();
    18641875            LBasicBlock powBlock = m_out.newBlock();
    18651876            LBasicBlock nanExceptionResultIsNaN = m_out.newBlock();
     
    18721883
    18731884            LBasicBlock lastNext = m_out.appendTo(integerExponentIsSmallBlock, integerExponentPowBlock);
    1874             LValue integerExponentBelow1000 = m_out.below(integerExponent, m_out.constInt32(1000));
    1875             m_out.branch(integerExponentBelow1000, usually(integerExponentPowBlock), rarely(doubleExponentPowBlockEntry));
     1885            LValue integerExponentBelowMax = m_out.belowOrEqual(integerExponent, m_out.constInt32(maxExponentForIntegerMathPow));
     1886            m_out.branch(integerExponentBelowMax, usually(integerExponentPowBlock), rarely(doubleExponentPowBlockEntry));
    18761887
    18771888            m_out.appendTo(integerExponentPowBlock, doubleExponentPowBlockEntry);
     
    18801891
    18811892            // If y is NaN, the result is NaN.
    1882             m_out.appendTo(doubleExponentPowBlockEntry, nanExceptionExponentIsInfinity);
     1893            m_out.appendTo(doubleExponentPowBlockEntry, nanExceptionBaseIsOne);
    18831894            LValue exponentIsNaN;
    18841895            if (provenType(m_node->child2()) & SpecDoubleNaN)
     
    18861897            else
    18871898                exponentIsNaN = m_out.booleanFalse;
    1888             m_out.branch(exponentIsNaN, rarely(nanExceptionResultIsNaN), usually(nanExceptionExponentIsInfinity));
     1899            m_out.branch(exponentIsNaN, rarely(nanExceptionResultIsNaN), usually(nanExceptionBaseIsOne));
    18891900
    18901901            // If abs(x) is 1 and y is +infinity, the result is NaN.
    18911902            // If abs(x) is 1 and y is -infinity, the result is NaN.
    1892             m_out.appendTo(nanExceptionExponentIsInfinity, nanExceptionBaseIsOne);
     1903
     1904            //     Test if base == 1.
     1905            m_out.appendTo(nanExceptionBaseIsOne, nanExceptionExponentIsInfinity);
     1906            LValue absoluteBase = m_out.doubleAbs(base);
     1907            LValue absoluteBaseIsOne = m_out.doubleEqual(absoluteBase, m_out.constDouble(1));
     1908            m_out.branch(absoluteBaseIsOne, rarely(nanExceptionExponentIsInfinity), usually(testExponentIsOneHalf));
     1909
     1910            //     Test if abs(y) == Infinity.
     1911            m_out.appendTo(nanExceptionExponentIsInfinity, testExponentIsOneHalf);
    18931912            LValue absoluteExponent = m_out.doubleAbs(exponent);
    18941913            LValue absoluteExponentIsInfinity = m_out.doubleEqual(absoluteExponent, m_out.constDouble(std::numeric_limits<double>::infinity()));
    1895             m_out.branch(absoluteExponentIsInfinity, rarely(nanExceptionBaseIsOne), usually(powBlock));
    1896 
    1897             m_out.appendTo(nanExceptionBaseIsOne, powBlock);
    1898             LValue absoluteBase = m_out.doubleAbs(base);
    1899             LValue absoluteBaseIsOne = m_out.doubleEqual(absoluteBase, m_out.constDouble(1));
    1900             m_out.branch(absoluteBaseIsOne, unsure(nanExceptionResultIsNaN), unsure(powBlock));
     1914            m_out.branch(absoluteExponentIsInfinity, rarely(nanExceptionResultIsNaN), usually(testExponentIsOneHalf));
     1915
     1916            // If y == 0.5 or y == -0.5, handle it through SQRT.
     1917            // We have be carefuly with -0 and -Infinity.
     1918
     1919            //     Test if y == 0.5
     1920            m_out.appendTo(testExponentIsOneHalf, handleBaseZeroExponentIsOneHalf);
     1921            LValue exponentIsOneHalf = m_out.doubleEqual(exponent, m_out.constDouble(0.5));
     1922            m_out.branch(exponentIsOneHalf, rarely(handleBaseZeroExponentIsOneHalf), usually(testExponentIsNegativeOneHalf));
     1923
     1924            //     Handle x == -0.
     1925            m_out.appendTo(handleBaseZeroExponentIsOneHalf, handleInfinityForExponentIsOneHalf);
     1926            LValue baseIsZeroExponentIsOneHalf = m_out.doubleEqual(base, m_out.doubleZero);
     1927            ValueFromBlock zeroResultExponentIsOneHalf = m_out.anchor(m_out.doubleZero);
     1928            m_out.branch(baseIsZeroExponentIsOneHalf, rarely(continuation), usually(handleInfinityForExponentIsOneHalf));
     1929
     1930            //     Test if abs(x) == Infinity.
     1931            m_out.appendTo(handleInfinityForExponentIsOneHalf, exponentIsOneHalfNormal);
     1932            LValue absoluteBaseIsInfinityOneHalf = m_out.doubleEqual(absoluteBase, m_out.constDouble(std::numeric_limits<double>::infinity()));
     1933            m_out.branch(absoluteBaseIsInfinityOneHalf, rarely(exponentIsOneHalfInfinity), usually(exponentIsOneHalfNormal));
     1934
     1935            //     The exponent is 0.5, the base is finite or NaN, we can use SQRT.
     1936            m_out.appendTo(exponentIsOneHalfNormal, exponentIsOneHalfInfinity);
     1937            ValueFromBlock sqrtResult = m_out.anchor(m_out.doubleSqrt(base));
     1938            m_out.jump(continuation);
     1939
     1940            //     The exponent is 0.5, the base is infinite, the result is always infinite.
     1941            m_out.appendTo(exponentIsOneHalfInfinity, testExponentIsNegativeOneHalf);
     1942            ValueFromBlock sqrtInfinityResult = m_out.anchor(m_out.constDouble(std::numeric_limits<double>::infinity()));
     1943            m_out.jump(continuation);
     1944
     1945            //     Test if y == -0.5
     1946            m_out.appendTo(testExponentIsNegativeOneHalf, testBaseZeroExponentIsNegativeOneHalf);
     1947            LValue exponentIsNegativeOneHalf = m_out.doubleEqual(exponent, m_out.constDouble(-0.5));
     1948            m_out.branch(exponentIsNegativeOneHalf, rarely(testBaseZeroExponentIsNegativeOneHalf), usually(powBlock));
     1949
     1950            //     Handle x == -0.
     1951            m_out.appendTo(testBaseZeroExponentIsNegativeOneHalf, handleBaseZeroExponentIsNegativeOneHalf);
     1952            LValue baseIsZeroExponentIsNegativeOneHalf = m_out.doubleEqual(base, m_out.doubleZero);
     1953            m_out.branch(baseIsZeroExponentIsNegativeOneHalf, rarely(handleBaseZeroExponentIsNegativeOneHalf), usually(handleInfinityForExponentIsNegativeOneHalf));
     1954
     1955            m_out.appendTo(handleBaseZeroExponentIsNegativeOneHalf, handleInfinityForExponentIsNegativeOneHalf);
     1956            ValueFromBlock oneOverSqrtZeroResult = m_out.anchor(m_out.constDouble(std::numeric_limits<double>::infinity()));
     1957            m_out.jump(continuation);
     1958
     1959            //     Test if abs(x) == Infinity.
     1960            m_out.appendTo(handleInfinityForExponentIsNegativeOneHalf, exponentIsNegativeOneHalfNormal);
     1961            LValue absoluteBaseIsInfinityNegativeOneHalf = m_out.doubleEqual(absoluteBase, m_out.constDouble(std::numeric_limits<double>::infinity()));
     1962            m_out.branch(absoluteBaseIsInfinityNegativeOneHalf, rarely(exponentIsNegativeOneHalfInfinity), usually(exponentIsNegativeOneHalfNormal));
     1963
     1964            //     The exponent is -0.5, the base is finite or NaN, we can use 1/SQRT.
     1965            m_out.appendTo(exponentIsNegativeOneHalfNormal, exponentIsNegativeOneHalfInfinity);
     1966            LValue sqrtBase = m_out.doubleSqrt(base);
     1967            ValueFromBlock oneOverSqrtResult = m_out.anchor(m_out.div(m_out.constDouble(1.), sqrtBase));
     1968            m_out.jump(continuation);
     1969
     1970            //     The exponent is -0.5, the base is infinite, the result is always zero.
     1971            m_out.appendTo(exponentIsNegativeOneHalfInfinity, powBlock);
     1972            ValueFromBlock oneOverSqrtInfinityResult = m_out.anchor(m_out.doubleZero);
     1973            m_out.jump(continuation);
    19011974
    19021975            m_out.appendTo(powBlock, nanExceptionResultIsNaN);
     
    19091982
    19101983            m_out.appendTo(continuation, lastNext);
    1911             setDouble(m_out.phi(m_out.doubleType, powDoubleIntResult, powResult, pureNan));
     1984            setDouble(m_out.phi(m_out.doubleType, powDoubleIntResult, zeroResultExponentIsOneHalf, sqrtResult, sqrtInfinityResult, oneOverSqrtZeroResult, oneOverSqrtResult, oneOverSqrtInfinityResult, powResult, pureNan));
    19121985        }
    19131986    }
  • trunk/Source/JavaScriptCore/jit/ThunkGenerators.cpp

    r199946 r200208  
    783783defineUnaryDoubleOpWrapper(trunc);
    784784
    785 static const double oneConstant = 1.0;
    786 static const double negativeHalfConstant = -0.5;
    787785static const double halfConstant = 0.5;
    788786   
     
    993991}
    994992
    995 MacroAssemblerCodeRef powThunkGenerator(VM* vm)
    996 {
    997     SpecializedThunkJIT jit(vm, 2);
    998     if (!jit.supportsFloatingPoint())
    999         return MacroAssemblerCodeRef::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm));
    1000 
    1001     jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), SpecializedThunkJIT::fpRegT1);
    1002     jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0);
    1003     MacroAssembler::Jump nonIntExponent;
    1004     jit.loadInt32Argument(1, SpecializedThunkJIT::regT0, nonIntExponent);
    1005     jit.appendFailure(jit.branch32(MacroAssembler::LessThan, SpecializedThunkJIT::regT0, MacroAssembler::TrustedImm32(0)));
    1006    
    1007     MacroAssembler::Jump exponentIsZero = jit.branchTest32(MacroAssembler::Zero, SpecializedThunkJIT::regT0);
    1008     MacroAssembler::Label startLoop(jit.label());
    1009 
    1010     MacroAssembler::Jump exponentIsEven = jit.branchTest32(MacroAssembler::Zero, SpecializedThunkJIT::regT0, MacroAssembler::TrustedImm32(1));
    1011     jit.mulDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1);
    1012     exponentIsEven.link(&jit);
    1013     jit.mulDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0);
    1014     jit.rshift32(MacroAssembler::TrustedImm32(1), SpecializedThunkJIT::regT0);
    1015     jit.branchTest32(MacroAssembler::NonZero, SpecializedThunkJIT::regT0).linkTo(startLoop, &jit);
    1016 
    1017     exponentIsZero.link(&jit);
    1018 
    1019     {
    1020         SpecializedThunkJIT::JumpList doubleResult;
    1021         jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT1, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT0);
    1022         jit.returnInt32(SpecializedThunkJIT::regT0);
    1023         doubleResult.link(&jit);
    1024         jit.returnDouble(SpecializedThunkJIT::fpRegT1);
    1025     }
    1026 
    1027     if (jit.supportsFloatingPointSqrt()) {
    1028         nonIntExponent.link(&jit);
    1029         jit.loadDouble(MacroAssembler::TrustedImmPtr(&negativeHalfConstant), SpecializedThunkJIT::fpRegT3);
    1030         jit.loadDoubleArgument(1, SpecializedThunkJIT::fpRegT2, SpecializedThunkJIT::regT0);
    1031         jit.appendFailure(jit.branchDouble(MacroAssembler::DoubleLessThanOrEqual, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1));
    1032         jit.appendFailure(jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, SpecializedThunkJIT::fpRegT2, SpecializedThunkJIT::fpRegT3));
    1033         jit.sqrtDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0);
    1034         jit.divDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1);
    1035 
    1036         SpecializedThunkJIT::JumpList doubleResult;
    1037         jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT1, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT0);
    1038         jit.returnInt32(SpecializedThunkJIT::regT0);
    1039         doubleResult.link(&jit);
    1040         jit.returnDouble(SpecializedThunkJIT::fpRegT1);
    1041     } else
    1042         jit.appendFailure(nonIntExponent);
    1043 
    1044     return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "pow");
    1045 }
    1046 
    1047993MacroAssemblerCodeRef imulThunkGenerator(VM* vm)
    1048994{
  • trunk/Source/JavaScriptCore/jit/ThunkGenerators.h

    r199946 r200208  
    6262MacroAssemblerCodeRef roundThunkGenerator(VM*);
    6363MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
    64 MacroAssemblerCodeRef powThunkGenerator(VM*);
    6564MacroAssemblerCodeRef imulThunkGenerator(VM*);
    6665MacroAssemblerCodeRef randomThunkGenerator(VM*);
  • trunk/Source/JavaScriptCore/runtime/MathCommon.cpp

    r199943 r200208  
    11/*
    2  * Copyright (C) 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    415415    if (std::isnan(y))
    416416        return PNaN;
    417     if (std::isinf(y) && fabs(x) == 1)
     417    double absoluteBase = fabs(x);
     418    if (absoluteBase == 1 && std::isinf(y))
    418419        return PNaN;
     420
     421    if (y == 0.5) {
     422        if (!absoluteBase)
     423            return 0;
     424        if (absoluteBase == std::numeric_limits<double>::infinity())
     425            return std::numeric_limits<double>::infinity();
     426        return sqrt(x);
     427    }
     428
     429    if (y == -0.5) {
     430        if (!absoluteBase)
     431            return std::numeric_limits<double>::infinity();
     432        if (absoluteBase == std::numeric_limits<double>::infinity())
     433            return 0.;
     434        return 1. / sqrt(x);
     435    }
     436
    419437    int32_t yAsInt = y;
    420     if (static_cast<double>(yAsInt) != y || yAsInt < 0)
    421         return mathPowInternal(x, y);
    422 
    423     // If the exponent is a positive int32 integer, we do a fast exponentiation
    424     double result = 1;
    425     while (yAsInt) {
    426         if (yAsInt & 1)
    427             result *= x;
    428         x *= x;
    429         yAsInt >>= 1;
    430     }
    431     return result;
     438    if (static_cast<double>(yAsInt) == y && yAsInt > 0 && yAsInt <= maxExponentForIntegerMathPow) {
     439        // If the exponent is a small positive int32 integer, we do a fast exponentiation
     440        double result = 1;
     441        while (yAsInt) {
     442            if (yAsInt & 1)
     443                result *= x;
     444            x *= x;
     445            yAsInt >>= 1;
     446        }
     447        return result;
     448
     449    }
     450    return mathPowInternal(x, y);
    432451}
    433452
  • trunk/Source/JavaScriptCore/runtime/MathCommon.h

    r199913 r200208  
    3636
    3737namespace JSC {
     38
     39const int32_t maxExponentForIntegerMathPow = 1000;
    3840double JIT_OPERATION operationMathPow(double x, double y) WTF_INTERNAL;
    3941
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r200177 r200208  
    475475    case SqrtIntrinsic:
    476476        return sqrtThunkGenerator;
    477     case PowIntrinsic:
    478         return powThunkGenerator;
    479477    case AbsIntrinsic:
    480478        return absThunkGenerator;
  • trunk/Source/JavaScriptCore/tests/stress/math-pow-with-constants.js

    r180360 r200208  
    4545
    4646function testPowUsedAsSqrt() {
    47     for (var i = 0; i < 10000; ++i) {
    48         var result = powUsedAsSqrt(4);
     47    for (let i = 0; i < 1e4; ++i) {
     48        let result = powUsedAsSqrt(4);
    4949        if (result !== Math.sqrt(4))
    5050            throw "Error: powUsedAsSqrt(4) should be 2, was = " + result;
    51     }
    52     for (var i = 0; i < 10000; ++i) {
    53         var result = powUsedAsSqrt(4.4);
     51        result = powUsedAsSqrt(4.4);
    5452        if (result !== Math.sqrt(4.4))
    5553            throw "Error: powUsedAsSqrt(4) should be " + Math.sqrt(4.4) + ", was = " + result;
    56     }
    57 
     54        if (powUsedAsSqrt(Infinity) !== Infinity)
     55            throw "Failed powUsedAsSqrt(Infinity)";
     56        if (powUsedAsSqrt(-Infinity) !== Infinity)
     57            throw "Failed powUsedAsSqrt(-Infinity)";
     58        let nanResult = powUsedAsSqrt(NaN)
     59        if (nanResult === nanResult)
     60            throw "Failed powUsedAsSqrt(NaN)";
     61        let zeroResult = powUsedAsSqrt(0.)
     62        if (zeroResult || (1 / zeroResult) !== Infinity)
     63            throw "Failed powUsedAsSqrt(0.)";
     64        let negativeZeroResult = powUsedAsSqrt(-0)
     65        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
     66            throw "Failed powUsedAsSqrt(-0)";
     67    }
    5868}
    5969testPowUsedAsSqrt();
    6070
     71function powUsedAsOneOverSqrt(x) {
     72    return Math.pow(x, -0.5);
     73}
     74noInline(powUsedAsOneOverSqrt);
     75
     76function testPowUsedAsOneOverSqrt() {
     77    for (let i = 0; i < 1e4; ++i) {
     78        let result = powUsedAsOneOverSqrt(4);
     79        if (result !== 0.5)
     80            throw "Error: powUsedAsOneOverSqrt(4) should be 0.5, was = " + result;
     81        result = powUsedAsOneOverSqrt(4.4);
     82        if (result !== 1/Math.sqrt(4.4))
     83            throw "Error: powUsedAsOneOverSqrt(4) should be " + 1/Math.sqrt(4.4) + ", was = " + result;
     84        if (powUsedAsOneOverSqrt(Infinity) !== 0)
     85            throw "Failed powUsedAsOneOverSqrt(Infinity)";
     86        if (powUsedAsOneOverSqrt(-Infinity) !== 0)
     87            throw "Failed powUsedAsOneOverSqrt(-Infinity)";
     88        let nanResult = powUsedAsOneOverSqrt(NaN)
     89        if (nanResult === nanResult)
     90            throw "Failed powUsedAsOneOverSqrt(NaN)";
     91        if (powUsedAsOneOverSqrt(0) !== Infinity)
     92            throw "Failed powUsedAsOneOverSqrt(0)";
     93        if (powUsedAsOneOverSqrt(-0.) !== Infinity)
     94            throw "Failed powUsedAsOneOverSqrt(-0.)";
     95    }
     96}
     97testPowUsedAsOneOverSqrt();
     98
     99function powUsedAsSquare(x) {
     100    return Math.pow(x, 2);
     101}
     102noInline(powUsedAsSquare);
     103
     104function testPowUsedAsSquare() {
     105    for (let i = 0; i < 1e4; ++i) {
     106        let result = powUsedAsSquare(2);
     107        if (result !== 4)
     108            throw "Error: powUsedAsSquare(4) should be 2, was = " + result;
     109        result = powUsedAsSquare(4.4);
     110        if (result !== 19.360000000000003)
     111            throw "Error: powUsedAsSquare(4) should be " + 19.360000000000003 + ", was = " + result;
     112        result = powUsedAsSquare(Math.PI);
     113        if (result !== 9.869604401089358)
     114            throw "Error: powUsedAsSquare(4) should be " + 9.869604401089358 + ", was = " + result;
     115        if (powUsedAsSquare(Infinity) !== Infinity)
     116            throw "Failed powUsedAsSquare(Infinity)";
     117        if (powUsedAsSquare(-Infinity) !== Infinity)
     118            throw "Failed powUsedAsSquare(-Infinity)";
     119        let nanResult = powUsedAsSquare(NaN)
     120        if (nanResult === nanResult)
     121            throw "Failed powUsedAsSquare(NaN)";
     122        let zeroResult = powUsedAsSquare(0.)
     123        if (zeroResult || (1 / zeroResult) !== Infinity)
     124            throw "Failed powUsedAsSquare(0.)";
     125        let negativeZeroResult = powUsedAsSquare(-0)
     126        if (negativeZeroResult || (1 / negativeZeroResult) !== Infinity)
     127            throw "Failed powUsedAsSquare(-0)";
     128    }
     129}
     130testPowUsedAsSquare();
    61131
    62132function intIntConstantsSmallNumbers() {
Note: See TracChangeset for help on using the changeset viewer.