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);
}