[DFG][FTL] Add ArithTan
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Sep 2016 22:01:13 +0000 (22:01 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Sep 2016 22:01:13 +0000 (22:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161857

Reviewed by Filip Pizlo.

JSTests:

* microbenchmarks/tan.js: Added.
* stress/arith-tan-on-various-types.js: Added.
(let.validInputTypedTestCases.validInputTestCases.map):
(isIdentical):
(opaqueTanNoArgument):
(testNoArgument):
(opaqueAllTypesTan):
(testAllTypesCall):
(testTangleTypeCall):
(testConstant):
(opaqueTanForSideEffects):
(testSideEffect.let.testObject.valueOf):
(testSideEffect):
(opaqueTanForCSE):
(testCSE.let.testObject.valueOf):
(testCSE):
(opaqueTanForDCE):
(testDCE.let.testObject.valueOf):
(testDCE):
(testException.opaqueTanWithException):
(testException):
* stress/ftl-arithtan.js: Added.
(foo):

Source/JavaScriptCore:

While ArithSin and ArithCos are supported, ArithTan is not supported yet.
And we also find that Math.tan is included in MotionMark's Multiply benchmark.

This patch adds ArithTan support in DFG and FTL. And it also cleans up the
existing ArithSin, ArithCos, and ArithLog compilations by unifying them.
The microbenchmark shows the 9% perf improvement.

    tan    322.4819+-0.3766     ^    295.8700+-0.3094        ^ definitely 1.0899x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithDoubleUnaryOp):
(JSC::DFG::SpeculativeJIT::compileArithCos):
(JSC::DFG::SpeculativeJIT::compileArithTan):
(JSC::DFG::SpeculativeJIT::compileArithSin):
(JSC::DFG::SpeculativeJIT::compileArithLog):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileArithTan):
* ftl/FTLOutput.cpp:
(JSC::FTL::Output::doubleTan):
* ftl/FTLOutput.h:
* runtime/Intrinsic.h:
* runtime/MathObject.cpp:
(JSC::MathObject::finishCreation):

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

25 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/tan.js [new file with mode: 0644]
JSTests/stress/arith-tan-on-various-types.js [new file with mode: 0644]
JSTests/stress/ftl-arithtan.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOutput.cpp
Source/JavaScriptCore/ftl/FTLOutput.h
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/MathObject.cpp

index 6c70668..f171005 100644 (file)
@@ -1,3 +1,34 @@
+2016-09-12  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG][FTL] Add ArithTan
+        https://bugs.webkit.org/show_bug.cgi?id=161857
+
+        Reviewed by Filip Pizlo.
+
+        * microbenchmarks/tan.js: Added.
+        * stress/arith-tan-on-various-types.js: Added.
+        (let.validInputTypedTestCases.validInputTestCases.map):
+        (isIdentical):
+        (opaqueTanNoArgument):
+        (testNoArgument):
+        (opaqueAllTypesTan):
+        (testAllTypesCall):
+        (testTangleTypeCall):
+        (testConstant):
+        (opaqueTanForSideEffects):
+        (testSideEffect.let.testObject.valueOf):
+        (testSideEffect):
+        (opaqueTanForCSE):
+        (testCSE.let.testObject.valueOf):
+        (testCSE):
+        (opaqueTanForDCE):
+        (testDCE.let.testObject.valueOf):
+        (testDCE):
+        (testException.opaqueTanWithException):
+        (testException):
+        * stress/ftl-arithtan.js: Added.
+        (foo):
+
 2016-09-12  Saam Barati  <sbarati@apple.com>
 
         MapHash should do constant folding when it has a constant argument and its legal to hash that value
diff --git a/JSTests/microbenchmarks/tan.js b/JSTests/microbenchmarks/tan.js
new file mode 100644 (file)
index 0000000..5d70516
--- /dev/null
@@ -0,0 +1,7 @@
+(function() {
+    for (var i = 0; i < 3000000; ++i)
+        x = Math.tan(i);
+
+    if (x != 1.8222665884307354)
+        throw "Error: bad result: " + x;
+})();
diff --git a/JSTests/stress/arith-tan-on-various-types.js b/JSTests/stress/arith-tan-on-various-types.js
new file mode 100644 (file)
index 0000000..4e99aea
--- /dev/null
@@ -0,0 +1,234 @@
+"use strict";
+
+let tanOfFour = Math.tan(4);
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0"],
+    ["4", "" + tanOfFour],
+    ["Math.PI", "" + Math.tan(Math.PI)],
+    ["Infinity", "NaN"],
+    ["-Infinity", "NaN"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "" + tanOfFour],
+    ["{ valueOf: () => { return 4; } }", "" + tanOfFour],
+];
+
+let validInputTypedTestCases = validInputTestCases.map((element) => { return [eval("(" + element[0] + ")"), eval(element[1])] });
+
+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;
+}
+
+
+// Test Math.tan() without arguments.
+function opaqueTanNoArgument() {
+    return Math.tan();
+}
+noInline(opaqueTanNoArgument);
+noOSRExitFuzzing(opaqueTanNoArgument);
+
+function testNoArgument() {
+    for (let i = 0; i < 1e4; ++i) {
+        let output = opaqueTanNoArgument();
+        if (output === output) {
+            throw "Failed opaqueTanNoArgument";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueTanNoArgument) > 1)
+        throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.tan() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesTan(argument) {
+    return Math.tan(argument);
+}
+noInline(opaqueAllTypesTan);
+noOSRExitFuzzing(opaqueAllTypesTan);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesTan(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesTan) > 2)
+        throw "We should have detected tan() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Test Math.tan() on a completely typed input. Every call see only one type.
+function testTangleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueTan(argument) {
+                return Math.tan(argument);
+            }
+            noInline(opaqueTan);
+            noOSRExitFuzzing(opaqueTan);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueTan(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testTangleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueTan) > 1)
+                throw "We should have compiled a tangle tan for the expected type.";
+        `);
+    }
+}
+testTangleTypeCall();
+
+
+// Test Math.tan() on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueTanOnConstant() {
+                return Math.tan(${testCaseInput[0]});
+            }
+            noInline(opaqueTanOnConstant);
+            noOSRExitFuzzing(opaqueTanOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueTanOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueTanOnConstant) > 1)
+                throw "We should have compiled a tangle tan for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueTanForSideEffects(argument) {
+    return Math.tan(argument);
+}
+noInline(opaqueTanForSideEffects);
+noOSRExitFuzzing(opaqueTanForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let tan16 = Math.tan(16);
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueTanForSideEffects(testObject) !== tan16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueTanForSideEffects) > 1)
+        throw "opaqueTanForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify tan() is not subject to CSE if the argument has side effects.
+function opaqueTanForCSE(argument) {
+    return Math.tan(argument) + Math.tan(argument) + Math.tan(argument);
+}
+noInline(opaqueTanForCSE);
+noOSRExitFuzzing(opaqueTanForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let tan16 = Math.tan(16);
+    let threeTan16 = tan16 + tan16 + tan16;
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueTanForCSE(testObject) !== threeTan16)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueTanForCSE) > 1)
+        throw "opaqueTanForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify tan() is not subject to DCE if the argument has side effects.
+function opaqueTanForDCE(argument) {
+    Math.tan(argument);
+}
+noInline(opaqueTanForDCE);
+noOSRExitFuzzing(opaqueTanForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueTanForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueTanForDCE) > 1)
+        throw "opaqueTanForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueTanWithException(argument) {
+        let result = Math.tan(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueTanWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+    let tan64 = Math.tan(64);
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueTanWithException(testObject) !== tan64)
+            throw "Incorrect result in opaqueTanWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueTanWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueTanWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();
diff --git a/JSTests/stress/ftl-arithtan.js b/JSTests/stress/ftl-arithtan.js
new file mode 100644 (file)
index 0000000..b8e4fc7
--- /dev/null
@@ -0,0 +1,13 @@
+function foo(x) {
+    return Math.tan(x);
+}
+
+noInline(foo);
+
+var j = 0;
+for (var i = 0; i < 100000; ++i)
+    j = foo(i);
+
+if (-1.6871736258025631 != j){
+    throw `Error: ${j}`;
+}
index 7d4c876..3fddd1a 100644 (file)
@@ -1,3 +1,58 @@
+2016-09-12  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG][FTL] Add ArithTan
+        https://bugs.webkit.org/show_bug.cgi?id=161857
+
+        Reviewed by Filip Pizlo.
+
+        While ArithSin and ArithCos are supported, ArithTan is not supported yet.
+        And we also find that Math.tan is included in MotionMark's Multiply benchmark.
+
+        This patch adds ArithTan support in DFG and FTL. And it also cleans up the
+        existing ArithSin, ArithCos, and ArithLog compilations by unifying them.
+        The microbenchmark shows the 9% perf improvement.
+
+            tan    322.4819+-0.3766     ^    295.8700+-0.3094        ^ definitely 1.0899x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileArithDoubleUnaryOp):
+        (JSC::DFG::SpeculativeJIT::compileArithCos):
+        (JSC::DFG::SpeculativeJIT::compileArithTan):
+        (JSC::DFG::SpeculativeJIT::compileArithSin):
+        (JSC::DFG::SpeculativeJIT::compileArithLog):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithTan):
+        * ftl/FTLOutput.cpp:
+        (JSC::FTL::Output::doubleTan):
+        * ftl/FTLOutput.h:
+        * runtime/Intrinsic.h:
+        * runtime/MathObject.cpp:
+        (JSC::MathObject::finishCreation):
+
 2016-09-12  Saam Barati  <sbarati@apple.com>
 
         MapHash should do constant folding when it has a constant argument and its legal to hash that value
index 8465837..e7af071 100644 (file)
@@ -959,6 +959,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         executeDoubleUnaryOpEffects(node, cos);
         break;
 
+    case ArithTan:
+        executeDoubleUnaryOpEffects(node, tan);
+        break;
+
     case ArithLog:
         executeDoubleUnaryOpEffects(node, log);
         break;
index 6af9427..80a1c2e 100644 (file)
@@ -2155,7 +2155,8 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
     case FRoundIntrinsic:
     case LogIntrinsic:
     case SinIntrinsic:
-    case SqrtIntrinsic: {
+    case SqrtIntrinsic:
+    case TanIntrinsic: {
         if (argumentCountIncludingThis == 1) {
             insertChecks();
             set(VirtualRegister(resultOperand), addToGraph(JSConstant, OpInfo(m_constantNaN)));
@@ -2179,6 +2180,9 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         case SqrtIntrinsic:
             nodeType = ArithSqrt;
             break;
+        case TanIntrinsic:
+            nodeType = ArithTan;
+            break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
         }
index cd7649b..40a0740 100644 (file)
@@ -188,6 +188,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ArithLog:
     case ArithSin:
     case ArithSqrt:
+    case ArithTan:
         if (node->child1().useKind() == DoubleRepUse)
             def(PureValue(node));
         else {
index 883b716..6e761b5 100644 (file)
@@ -94,6 +94,7 @@ bool doesGC(Graph& graph, Node* node)
     case ArithFRound:
     case ArithSin:
     case ArithCos:
+    case ArithTan:
     case ArithLog:
     case ValueAdd:
     case TryGetById:
index ee915c6..d3211c5 100644 (file)
@@ -401,7 +401,8 @@ private:
         case ArithFRound:
         case ArithLog:
         case ArithSin:
-        case ArithSqrt: {
+        case ArithSqrt:
+        case ArithTan: {
             Edge& child1 = node->child1();
             if (child1->shouldSpeculateNotCell()) {
                 fixDoubleOrBooleanEdge(child1);
index 24047bc..dca1ca5 100644 (file)
@@ -162,6 +162,7 @@ namespace JSC { namespace DFG {
     macro(ArithSqrt, NodeResultDouble | NodeMustGenerate) \
     macro(ArithSin, NodeResultDouble | NodeMustGenerate) \
     macro(ArithCos, NodeResultDouble | NodeMustGenerate) \
+    macro(ArithTan, NodeResultDouble | NodeMustGenerate) \
     macro(ArithLog, NodeResultDouble | NodeMustGenerate) \
     \
     /* Add of values may either be arithmetic, or result in string concatenation. */\
index 2f9f329..562c8a4 100644 (file)
@@ -429,6 +429,19 @@ double JIT_OPERATION operationArithSqrt(ExecState* exec, EncodedJSValue encodedO
     return sqrt(a);
 }
 
+double JIT_OPERATION operationArithTan(ExecState* exec, EncodedJSValue encodedOp1)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+    double a = op1.toNumber(exec);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(JSValue());
+    return tan(a);
+}
+
 static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
 {
     VM& vm = exec->vm();
index dd40ca4..86e191e 100644 (file)
@@ -56,6 +56,7 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState*, EncodedJSValue encode
 double JIT_OPERATION operationArithAbs(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 int32_t JIT_OPERATION operationArithClz32(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithCos(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
+double JIT_OPERATION operationArithTan(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithFRound(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithLog(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithSin(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
index 62b6719..9344f62 100644 (file)
@@ -561,6 +561,7 @@ private:
         case ArithSqrt:
         case ArithCos:
         case ArithSin:
+        case ArithTan:
         case ArithLog:
             if (node->child1()->shouldSpeculateNumber())
                 m_graph.voteNode(node->child1(), VoteDouble, weight);
@@ -769,6 +770,7 @@ private:
         case ArithFRound:
         case ArithSin:
         case ArithCos:
+        case ArithTan:
         case ArithLog: {
             setPrediction(SpecBytecodeDouble);
             break;
index 68af2fa..fd76950 100644 (file)
@@ -191,6 +191,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ArithTrunc:
     case ArithSin:
     case ArithCos:
+    case ArithTan:
     case ArithLog:
     case ValueAdd:
     case TryGetById:
index 954e54b..d052089 100644 (file)
@@ -3951,16 +3951,16 @@ void SpeculativeJIT::compileArithClz32(Node* node)
     int32Result(resultReg, node);
 }
 
-void SpeculativeJIT::compileArithCos(Node* node)
+void SpeculativeJIT::compileArithDoubleUnaryOp(Node* node, double (*doubleFunction)(double), double (*operation)(ExecState*, EncodedJSValue))
 {
     if (node->child1().useKind() == DoubleRepUse) {
         SpeculateDoubleOperand op1(this, node->child1());
         FPRReg op1FPR = op1.fpr();
 
         flushRegisters();
-        
+
         FPRResult result(this);
-        callOperation(cos, result.fpr(), op1FPR);
+        callOperation(doubleFunction, result.fpr(), op1FPR);
         doubleResult(result.fpr(), node);
         return;
     }
@@ -3969,11 +3969,21 @@ void SpeculativeJIT::compileArithCos(Node* node)
     JSValueRegs op1Regs = op1.jsValueRegs();
     flushRegisters();
     FPRResult result(this);
-    callOperation(operationArithCos, result.fpr(), op1Regs);
+    callOperation(operation, result.fpr(), op1Regs);
     m_jit.exceptionCheck();
     doubleResult(result.fpr(), node);
 }
 
+void SpeculativeJIT::compileArithCos(Node* node)
+{
+    compileArithDoubleUnaryOp(node, cos, operationArithCos);
+}
+
+void SpeculativeJIT::compileArithTan(Node* node)
+{
+    compileArithDoubleUnaryOp(node, tan, operationArithTan);
+}
+
 void SpeculativeJIT::compileArithSub(Node* node)
 {
     switch (node->binaryUseKind()) {
@@ -4999,25 +5009,7 @@ void SpeculativeJIT::compileArithRounding(Node* node)
 
 void SpeculativeJIT::compileArithSin(Node* node)
 {
-    if (node->child1().useKind() == DoubleRepUse) {
-        SpeculateDoubleOperand op1(this, node->child1());
-        FPRReg op1FPR = op1.fpr();
-
-        flushRegisters();
-        
-        FPRResult result(this);
-        callOperation(sin, result.fpr(), op1FPR);
-        doubleResult(result.fpr(), node);
-        return;
-    }
-
-    JSValueOperand op1(this, node->child1());
-    JSValueRegs op1Regs = op1.jsValueRegs();
-    flushRegisters();
-    FPRResult result(this);
-    callOperation(operationArithSin, result.fpr(), op1Regs);
-    m_jit.exceptionCheck();
-    doubleResult(result.fpr(), node);
+    compileArithDoubleUnaryOp(node, sin, operationArithSin);
 }
 
 void SpeculativeJIT::compileArithSqrt(Node* node)
@@ -5198,23 +5190,7 @@ void SpeculativeJIT::compileArithPow(Node* node)
 
 void SpeculativeJIT::compileArithLog(Node* node)
 {
-    if (node->child1().useKind() == DoubleRepUse) {
-        SpeculateDoubleOperand op1(this, node->child1());
-        FPRReg op1FPR = op1.fpr();
-        flushRegisters();
-        FPRResult result(this);
-        callOperation(log, result.fpr(), op1FPR);
-        doubleResult(result.fpr(), node);
-        return;
-    }
-
-    JSValueOperand op1(this, node->child1());
-    JSValueRegs op1Regs = op1.jsValueRegs();
-    flushRegisters();
-    FPRResult result(this);
-    callOperation(operationArithLog, result.fpr(), op1Regs);
-    m_jit.exceptionCheck();
-    doubleResult(result.fpr(), node);
+    compileArithDoubleUnaryOp(node, log, operationArithLog);
 }
 
 // Returns true if the compare is fused with a subsequent branch.
index b858c09..9a77732 100644 (file)
@@ -2507,12 +2507,14 @@ public:
     template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction>
     void compileMathIC(Node*, JITMathIC<Generator>*, bool needsScratchGPRReg, bool needsScratchFPRReg, RepatchingFunction, NonRepatchingFunction);
 
+    void compileArithDoubleUnaryOp(Node*, double (*doubleFunction)(double), double (*operation)(ExecState*, EncodedJSValue));
     void compileValueAdd(Node*);
     void compileArithAdd(Node*);
     void compileMakeRope(Node*);
     void compileArithAbs(Node*);
     void compileArithClz32(Node*);
     void compileArithCos(Node*);
+    void compileArithTan(Node*);
     void compileArithSub(Node*);
     void compileArithNegate(Node*);
     void compileArithMul(Node*);
index 70d12ee..4715398 100644 (file)
@@ -2348,6 +2348,10 @@ void SpeculativeJIT::compile(Node* node)
         compileArithCos(node);
         break;
 
+    case ArithTan:
+        compileArithTan(node);
+        break;
+
     case ArithLog:
         compileArithLog(node);
         break;
index b9f9512..06385df 100644 (file)
@@ -2503,6 +2503,10 @@ void SpeculativeJIT::compile(Node* node)
         compileArithCos(node);
         break;
 
+    case ArithTan:
+        compileArithTan(node);
+        break;
+
     case ArithLog:
         compileArithLog(node);
         break;
index 8217783..50f6c0a 100644 (file)
@@ -94,6 +94,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ArithAbs:
     case ArithSin:
     case ArithCos:
+    case ArithTan:
     case ArithPow:
     case ArithRandom:
     case ArithRound:
index 2262ddc..42d185a 100644 (file)
@@ -541,6 +541,9 @@ private:
         case ArithCos:
             compileArithCos();
             break;
+        case ArithTan:
+            compileArithTan();
+            break;
         case ArithPow:
             compileArithPow();
             break;
@@ -2066,6 +2069,17 @@ private:
         setDouble(result);
     }
 
+    void compileArithTan()
+    {
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            setDouble(m_out.doubleTan(lowDouble(m_node->child1())));
+            return;
+        }
+        LValue argument = lowJSValue(m_node->child1());
+        LValue result = vmCall(Double, m_out.operation(operationArithTan), m_callFrame, argument);
+        setDouble(result);
+    }
+
     void compileArithPow()
     {
         if (m_node->child2().useKind() == Int32Use)
index 2b1f163..c561380 100644 (file)
@@ -299,6 +299,12 @@ LValue Output::doubleCos(LValue value)
     return callWithoutSideEffects(B3::Double, cosDouble, value);
 }
 
+LValue Output::doubleTan(LValue value)
+{
+    double (*tanDouble)(double) = tan;
+    return callWithoutSideEffects(B3::Double, tanDouble, value);
+}
+
 LValue Output::doublePow(LValue xOperand, LValue yOperand)
 {
     double (*powDouble)(double, double) = pow;
index d0c867b..7b28e54 100644 (file)
@@ -163,6 +163,7 @@ public:
 
     LValue doubleSin(LValue);
     LValue doubleCos(LValue);
+    LValue doubleTan(LValue);
 
     LValue doublePow(LValue base, LValue exponent);
     LValue doublePowi(LValue base, LValue exponent);
index d988110..33f2d62 100644 (file)
@@ -38,6 +38,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     SinIntrinsic,
     Clz32Intrinsic,
     CosIntrinsic,
+    TanIntrinsic,
     ArrayPushIntrinsic,
     ArrayPopIntrinsic,
     CharCodeAtIntrinsic,
index 3db16f8..d0406cc 100644 (file)
@@ -120,7 +120,7 @@ void MathObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sin"), 1, mathProtoFuncSin, SinIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sinh"), 1, mathProtoFuncSinh, NoIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sqrt"), 1, mathProtoFuncSqrt, SqrtIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tan"), 1, mathProtoFuncTan, NoIntrinsic, DontEnum);
+    putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tan"), 1, mathProtoFuncTan, TanIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "tanh"), 1, mathProtoFuncTanh, NoIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "trunc"), 1, mathProtoFuncTrunc, TruncIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "imul"), 2, mathProtoFuncIMul, IMulIntrinsic, DontEnum);