[JSC] ArithSqrt should work with any argument type
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Aug 2016 02:00:44 +0000 (02:00 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Aug 2016 02:00:44 +0000 (02:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160954

Patch by Benjamin Poulain <bpoulain@apple.com> on 2016-08-19
Reviewed by Saam Barati.

JSTests:

* stress/arith-sqrt-on-various-types.js: Added.
(let.validInputTypedTestCases.validInputTestCases.map):
(isIdentical):
(opaqueAllTypesSqrt):
(testAllTypesCall):
(testSingleTypeCall):
(opaqueSqrtForSideEffects):
(testSideEffect.let.testObject.valueOf):
(testSideEffect):
(opaqueSqrtForCSE):
(testCSE.let.testObject.valueOf):
(testCSE):
(testException.opaqueSqrtWithException):
(testException):

Source/JavaScriptCore:

Previsouly, ArithSqrt would always OSR Exit if the argument
is not typed Integer, Double, or Boolean.
Since we can't recover by generalizing to those, we continuously
OSR Exit and recompile the same code over and over again.

This patch introduces a fallback to handle the remaining types.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):

* dfg/DFGMayExit.cpp:
This is somewhat unrelated. While discussing the design of this
with Filip, we decided not to use ToNumber+ArithSqrt despite
the guarantee that ToNumber does not OSR Exit.
Since it does not OSR Exit, we should say so in mayExitImpl().

* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithSqrt):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileArithSqrt):

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

21 files changed:
JSTests/ChangeLog
JSTests/stress/arith-sqrt-on-various-types.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRExitFuzz.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h
Source/JavaScriptCore/runtime/TestRunnerUtils.cpp
Source/JavaScriptCore/runtime/TestRunnerUtils.h

index 7ed0d48..0f962b4 100644 (file)
@@ -1,3 +1,25 @@
+2016-08-19  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] ArithSqrt should work with any argument type
+        https://bugs.webkit.org/show_bug.cgi?id=160954
+
+        Reviewed by Saam Barati.
+
+        * stress/arith-sqrt-on-various-types.js: Added.
+        (let.validInputTypedTestCases.validInputTestCases.map):
+        (isIdentical):
+        (opaqueAllTypesSqrt):
+        (testAllTypesCall):
+        (testSingleTypeCall):
+        (opaqueSqrtForSideEffects):
+        (testSideEffect.let.testObject.valueOf):
+        (testSideEffect):
+        (opaqueSqrtForCSE):
+        (testCSE.let.testObject.valueOf):
+        (testCSE):
+        (testException.opaqueSqrtWithException):
+        (testException):
+
 2016-08-19  Joseph Pecoraro  <pecoraro@apple.com>
 
         Make custom Error properties (line, column, sourceURL) configurable and writable
diff --git a/JSTests/stress/arith-sqrt-on-various-types.js b/JSTests/stress/arith-sqrt-on-various-types.js
new file mode 100644 (file)
index 0000000..0060a0a
--- /dev/null
@@ -0,0 +1,162 @@
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0."],
+    ["4", "2"],
+    ["42.5", "6.519202405202649"],
+    ["Infinity", "Infinity"],
+    ["-Infinity", "NaN"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "2"],
+    ["{ valueOf: () => { return 4; } }", "2"],
+];
+
+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.sqrt() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesSqrt(argument) {
+    return Math.sqrt(argument);
+}
+noInline(opaqueAllTypesSqrt);
+noOSRExitFuzzing(opaqueAllTypesSqrt);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesSqrt(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesSqrt) > 2)
+        throw "We should have detected sqrt() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Test Math.sqrt() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueSqrt(argument) {
+                return Math.sqrt(argument);
+            }
+            noInline(opaqueSqrt);
+            noOSRExitFuzzing(opaqueSqrt);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueSqrt(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueSqrt) > 1)
+                throw "We should have compiled a single sqrt for the expected type.";
+        `);
+    }
+}
+testSingleTypeCall();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueSqrtForSideEffects(argument) {
+    return Math.sqrt(argument);
+}
+noInline(opaqueSqrtForSideEffects);
+noOSRExitFuzzing(opaqueSqrtForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueSqrtForSideEffects(testObject) !== 4)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueSqrtForSideEffects) > 1)
+        throw "opaqueSqrtForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify SQRT is not subject to CSE if the argument has side effects.
+function opaqueSqrtForCSE(argument) {
+    return Math.sqrt(argument) + Math.sqrt(argument) + Math.sqrt(argument);
+}
+noInline(opaqueSqrtForCSE);
+noOSRExitFuzzing(opaqueSqrtForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueSqrtForCSE(testObject) !== 12)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueSqrtForCSE) > 1)
+        throw "opaqueSqrtForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueSqrtWithException(argument) {
+        let result = Math.sqrt(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueSqrtWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueSqrtWithException(testObject) !== 8)
+            throw "Incorrect result in opaqueSqrtWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueSqrtWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueSqrtWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();
index 64bcf10..84b253a 100644 (file)
@@ -1,3 +1,40 @@
+2016-08-19  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] ArithSqrt should work with any argument type
+        https://bugs.webkit.org/show_bug.cgi?id=160954
+
+        Reviewed by Saam Barati.
+
+        Previsouly, ArithSqrt would always OSR Exit if the argument
+        is not typed Integer, Double, or Boolean.
+        Since we can't recover by generalizing to those, we continuously
+        OSR Exit and recompile the same code over and over again.
+
+        This patch introduces a fallback to handle the remaining types.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+
+        * dfg/DFGMayExit.cpp:
+        This is somewhat unrelated. While discussing the design of this
+        with Filip, we decided not to use ToNumber+ArithSqrt despite
+        the guarantee that ToNumber does not OSR Exit.
+        Since it does not OSR Exit, we should say so in mayExitImpl().
+
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileArithSqrt):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithSqrt):
+
 2016-08-19  Joseph Pecoraro  <pecoraro@apple.com>
 
         Make custom Error properties (line, column, sourceURL) configurable and writable
index fc8446c..a7d701a 100644 (file)
@@ -961,7 +961,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(sqrt(child.asNumber())));
             break;
         }
-        forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
+        SpeculatedType sqrtType = SpecFullNumber;
+        if (node->child1().useKind() == DoubleRepUse)
+            sqrtType = typeOfDoubleUnaryOp(forNode(node->child1()).m_type);
+        forNode(node).setType(sqrtType);
         break;
     }
         
index c899400..3688d9b 100644 (file)
@@ -91,6 +91,10 @@ bool mightInlineFunctionForConstruct(CodeBlock* codeBlock)
     return codeBlock->instructionCount() <= Options::maximumFunctionForConstructInlineCandidateInstructionCount()
         && isSupportedForInlining(codeBlock);
 }
+bool canUseOSRExitFuzzing(CodeBlock* codeBlock)
+{
+    return codeBlock->ownerScriptExecutable()->canUseOSRExitFuzzing();
+}
 
 inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, CapabilityLevel result)
 {
index 4010bb2..04938bf 100644 (file)
@@ -47,6 +47,7 @@ bool mightCompileFunctionForConstruct(CodeBlock*);
 bool mightInlineFunctionForCall(CodeBlock*);
 bool mightInlineFunctionForClosureCall(CodeBlock*);
 bool mightInlineFunctionForConstruct(CodeBlock*);
+bool canUseOSRExitFuzzing(CodeBlock*);
 
 inline CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruction* pc);
 
@@ -59,6 +60,7 @@ inline bool mightCompileFunctionForConstruct(CodeBlock*) { return false; }
 inline bool mightInlineFunctionForCall(CodeBlock*) { return false; }
 inline bool mightInlineFunctionForClosureCall(CodeBlock*) { return false; }
 inline bool mightInlineFunctionForConstruct(CodeBlock*) { return false; }
+inline bool canUseOSRExitFuzzing(CodeBlock*) { return false; }
 
 inline CapabilityLevel capabilityLevel(OpcodeID, CodeBlock*, Instruction*) { return CannotCompile; }
 inline CapabilityLevel capabilityLevel(CodeBlock*) { return CannotCompile; }
index c2a82db..ac356f8 100644 (file)
@@ -154,7 +154,6 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ArithMin:
     case ArithMax:
     case ArithPow:
-    case ArithSqrt:
     case ArithFRound:
     case ArithSin:
     case ArithCos:
@@ -190,6 +189,15 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(PureValue(node));
         return;
 
+    case ArithSqrt:
+        if (node->child1().useKind() == DoubleRepUse)
+            def(PureValue(node));
+        else {
+            read(World);
+            write(Heap);
+        }
+        return;
+
     case BitAnd:
     case BitOr:
     case BitXor:
index 26e5cb5..3bb156b 100644 (file)
@@ -386,7 +386,14 @@ private:
             break;
         }
             
-        case ArithSqrt:
+        case ArithSqrt: {
+            Edge& child1 = node->child1();
+            if (child1->shouldSpeculateNumberOrBoolean())
+                fixDoubleOrBooleanEdge(child1);
+            else
+                fixEdge<UntypedUse>(child1);
+            break;
+        }
         case ArithFRound:
         case ArithSin:
         case ArithCos:
index 43bdc36..68df0aa 100644 (file)
@@ -100,12 +100,13 @@ ExitMode mayExitImpl(Graph& graph, Node* node, StateType& state)
     case ConstructVarargs:
     case CallForwardVarargs:
     case ConstructForwardVarargs:
+    case CreateActivation:
     case MaterializeCreateActivation:
     case MaterializeNewObject:
     case NewFunction:
     case NewGeneratorFunction:
     case NewStringObject:
-    case CreateActivation:
+    case ToNumber:
         result = ExitsForExceptions;
         break;
 
index 67d49ee..2f2ff7d 100644 (file)
@@ -159,7 +159,7 @@ namespace JSC { namespace DFG {
     macro(ArithFloor, NodeResultNumber) \
     macro(ArithCeil, NodeResultNumber) \
     macro(ArithTrunc, NodeResultNumber) \
-    macro(ArithSqrt, NodeResultNumber) \
+    macro(ArithSqrt, NodeResultDouble) \
     macro(ArithSin, NodeResultNumber) \
     macro(ArithCos, NodeResultNumber) \
     macro(ArithLog, NodeResultNumber) \
index 8121f1c..26c0553 100644 (file)
@@ -34,9 +34,8 @@ extern unsigned g_numberOfStaticOSRExitFuzzChecks;
 
 inline bool doOSRExitFuzzing()
 {
-    if (!Options::useOSRExitFuzz())
-        return false;
-    
+    ASSERT(Options::useOSRExitFuzz());
+
     g_numberOfStaticOSRExitFuzzChecks++;
     if (unsigned atStatic = Options::fireOSRExitFuzzAtStatic())
         return atStatic == g_numberOfStaticOSRExitFuzzChecks;
index 676ca26..5d90039 100644 (file)
@@ -322,6 +322,18 @@ EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue e
     return JSValue::encode(jsNumber(a / b));
 }
 
+double JIT_OPERATION operationArithSqrt(ExecState* exec, EncodedJSValue encodedOp1)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    JSValue op1 = JSValue::decode(encodedOp1);
+    double a = op1.toNumber(exec);
+    if (UNLIKELY(vm->exception()))
+        return JSValue::encode(JSValue());
+    return sqrt(a);
+}
+
 static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
 {
     VM& vm = exec->vm();
index 77cd8ea..904ec0a 100644 (file)
@@ -53,6 +53,7 @@ EncodedJSValue JIT_OPERATION operationValueBitRShift(ExecState*, EncodedJSValue
 EncodedJSValue JIT_OPERATION operationValueBitURShift(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueDiv(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+double JIT_OPERATION operationArithSqrt(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
index 76b5567..3d11c1e 100644 (file)
@@ -33,6 +33,7 @@
 #include "DFGArrayifySlowPathGenerator.h"
 #include "DFGCallArrayAllocatorSlowPathGenerator.h"
 #include "DFGCallCreateDirectArgumentsSlowPathGenerator.h"
+#include "DFGCapabilities.h"
 #include "DFGMayExit.h"
 #include "DFGOSRExitFuzz.h"
 #include "DFGSaneStringGetByValSlowPathGenerator.h"
@@ -193,7 +194,9 @@ void SpeculativeJIT::emitGetArgumentStart(CodeOrigin origin, GPRReg startGPR)
 
 MacroAssembler::Jump SpeculativeJIT::emitOSRExitFuzzCheck()
 {
-    if (!doOSRExitFuzzing())
+    if (!Options::useOSRExitFuzz()
+        || !canUseOSRExitFuzzing(m_jit.graph().baselineCodeBlockFor(m_origin.semantic))
+        || !doOSRExitFuzzing())
         return MacroAssembler::Jump();
     
     MacroAssembler::Jump result;
@@ -4887,19 +4890,30 @@ void SpeculativeJIT::compileArithRounding(Node* node)
 
 void SpeculativeJIT::compileArithSqrt(Node* node)
 {
-    SpeculateDoubleOperand op1(this, node->child1());
-    FPRReg op1FPR = op1.fpr();
+    if (node->child1().useKind() == DoubleRepUse) {
+        SpeculateDoubleOperand op1(this, node->child1());
+        FPRReg op1FPR = op1.fpr();
 
-    if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::useArchitectureSpecificOptimizations()) {
-        flushRegisters();
-        FPRResult result(this);
-        callOperation(sqrt, result.fpr(), op1FPR);
-        doubleResult(result.fpr(), node);
-    } else {
-        FPRTemporary result(this, op1);
-        m_jit.sqrtDouble(op1.fpr(), result.fpr());
-        doubleResult(result.fpr(), node);
+        if (!MacroAssembler::supportsFloatingPointSqrt() || !Options::useArchitectureSpecificOptimizations()) {
+            flushRegisters();
+            FPRResult result(this);
+            callOperation(sqrt, result.fpr(), op1FPR);
+            doubleResult(result.fpr(), node);
+        } else {
+            FPRTemporary result(this, op1);
+            m_jit.sqrtDouble(op1.fpr(), result.fpr());
+            doubleResult(result.fpr(), node);
+        }
+        return;
     }
+
+    JSValueOperand op1(this, node->child1());
+    JSValueRegs op1Regs = op1.jsValueRegs();
+    flushRegisters();
+    FPRResult result(this);
+    callOperation(operationArithSqrt, result.fpr(), op1Regs);
+    m_jit.exceptionCheck();
+    doubleResult(result.fpr(), node);
 }
 
 // For small positive integers , it is worth doing a tiny inline loop to exponentiate the base.
index 47d18c5..a40a669 100644 (file)
@@ -1712,6 +1712,11 @@ public:
         m_jit.setupArgumentsWithExecState(regOp1, TrustedImmPtr(identOp2), TrustedImm32(op3), regOp4, regOp5);
         return appendCall(operation);
     }
+    JITCompiler::Call callOperation(D_JITOperation_EJ operation, FPRReg result, JSValueRegs arg1)
+    {
+        m_jit.setupArgumentsWithExecState(arg1.gpr());
+        return appendCallSetResult(operation, result);
+    }
 #else // USE(JSVALUE32_64)
 
     JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
@@ -2138,6 +2143,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(identOp2), TrustedImm32(op3), arg4, arg5);
         return appendCall(operation);
     }
+    JITCompiler::Call callOperation(D_JITOperation_EJ operation, FPRReg result, JSValueRegs arg1)
+    {
+        m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR());
+        return appendCallSetResult(operation, result);
+    }
 #endif // USE(JSVALUE32_64)
     
 #if !defined(NDEBUG) && !CPU(ARM) && !CPU(MIPS) && !CPU(SH4)
index 4896e6d..edfb2f8 100644 (file)
@@ -38,6 +38,7 @@
 #include "CallFrameShuffler.h"
 #include "CodeBlockWithJITType.h"
 #include "DFGAbstractInterpreterInlines.h"
+#include "DFGCapabilities.h"
 #include "DFGDominators.h"
 #include "DFGInPlaceAbstractState.h"
 #include "DFGOSRAvailabilityAnalysisPhase.h"
@@ -2272,7 +2273,16 @@ private:
             setDouble(result);
     }
 
-    void compileArithSqrt() { setDouble(m_out.doubleSqrt(lowDouble(m_node->child1()))); }
+    void compileArithSqrt()
+    {
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            setDouble(m_out.doubleSqrt(lowDouble(m_node->child1())));
+            return;
+        }
+        LValue argument = lowJSValue(m_node->child1());
+        LValue result = vmCall(Double, m_out.operation(operationArithSqrt), m_callFrame, argument);
+        setDouble(result);
+    }
 
     void compileArithLog() { setDouble(m_out.doubleLog(lowDouble(m_node->child1()))); }
     
@@ -11058,8 +11068,11 @@ private:
         }
 
         DFG_ASSERT(m_graph, m_node, origin.exitOK);
-        
-        if (doOSRExitFuzzing() && !isExceptionHandler) {
+
+        if (!isExceptionHandler
+            && Options::useOSRExitFuzz()
+            && canUseOSRExitFuzzing(m_graph.baselineCodeBlockFor(m_node->origin.semantic))
+            && doOSRExitFuzzing()) {
             LValue numberOfFuzzChecks = m_out.add(
                 m_out.load32(m_out.absolute(&g_numberOfOSRExitFuzzChecks)),
                 m_out.int32One);
index 80aa2a7..b8eb158 100644 (file)
@@ -598,6 +598,7 @@ static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionNoDFG(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionNoFTL(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*);
@@ -795,6 +796,7 @@ protected:
         addFunction(vm, "noInline", functionNeverInlineFunction, 1);
         addFunction(vm, "noDFG", functionNoDFG, 1);
         addFunction(vm, "noFTL", functionNoFTL, 1);
+        addFunction(vm, "noOSRExitFuzzing", functionNoOSRExitFuzzing, 1);
         addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
         addFunction(vm, "optimizeNextInvocation", functionOptimizeNextInvocation, 1);
         addFunction(vm, "reoptimizationRetryCount", functionReoptimizationRetryCount, 1);
@@ -1580,6 +1582,11 @@ EncodedJSValue JSC_HOST_CALL functionNoFTL(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState* exec)
+{
+    return JSValue::encode(setCannotUseOSRExitFuzzing(exec));
+}
+
 EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState* exec)
 {
     return JSValue::encode(optimizeNextInvocation(exec));
index 4c9449d..589ea59 100644 (file)
@@ -146,6 +146,7 @@ ScriptExecutable::ScriptExecutable(Structure* structure, VM& vm, const SourceCod
     , m_neverOptimize(false)
     , m_neverFTLOptimize(false)
     , m_isArrowFunctionContext(isInArrowFunctionContext)
+    , m_canUseOSRExitFuzzing(true)
     , m_derivedContextType(static_cast<unsigned>(derivedContextType))
     , m_evalContextType(static_cast<unsigned>(evalContextType))
     , m_overrideLineNumber(-1)
index 2d00919..35b2d4c 100644 (file)
@@ -337,12 +337,14 @@ public:
     void setNeverOptimize(bool value) { m_neverOptimize = value; }
     void setNeverFTLOptimize(bool value) { m_neverFTLOptimize = value; }
     void setDidTryToEnterInLoop(bool value) { m_didTryToEnterInLoop = value; }
+    void setCanUseOSRExitFuzzing(bool value) { m_canUseOSRExitFuzzing = value; }
     bool neverInline() const { return m_neverInline; }
     bool neverOptimize() const { return m_neverOptimize; }
     bool neverFTLOptimize() const { return m_neverFTLOptimize; }
     bool didTryToEnterInLoop() const { return m_didTryToEnterInLoop; }
     bool isInliningCandidate() const { return !neverInline(); }
     bool isOkToOptimize() const { return !neverOptimize(); }
+    bool canUseOSRExitFuzzing() const { return m_canUseOSRExitFuzzing; }
     
     bool* addressOfDidTryToEnterInLoop() { return &m_didTryToEnterInLoop; }
 
@@ -403,6 +405,7 @@ protected:
     bool m_neverOptimize : 1;
     bool m_neverFTLOptimize : 1;
     bool m_isArrowFunctionContext : 1;
+    bool m_canUseOSRExitFuzzing : 1;
     unsigned m_derivedContextType : 2; // DerivedContextType
     unsigned m_evalContextType : 2; // EvalContextType
 
index d13090f..43aa456 100644 (file)
@@ -131,6 +131,18 @@ JSValue setNeverOptimize(ExecState* exec)
     return setNeverOptimize(exec->uncheckedArgument(0));
 }
 
+JSValue setCannotUseOSRExitFuzzing(ExecState* exec)
+{
+    if (exec->argumentCount() < 1)
+        return jsUndefined();
+
+    JSValue theFunctionValue = exec->uncheckedArgument(0);
+    if (FunctionExecutable* executable = getExecutableForFunction(theFunctionValue))
+        executable->setCanUseOSRExitFuzzing(false);
+
+    return jsUndefined();
+}
+
 JSValue optimizeNextInvocation(ExecState* exec)
 {
     if (exec->argumentCount() < 1)
index b8dfaea..14658d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -45,6 +45,7 @@ JS_EXPORT_PRIVATE JSValue failNextNewCodeBlock(ExecState*);
 JS_EXPORT_PRIVATE JSValue numberOfDFGCompiles(ExecState*);
 JS_EXPORT_PRIVATE JSValue setNeverInline(ExecState*);
 JS_EXPORT_PRIVATE JSValue setNeverOptimize(ExecState*);
+JS_EXPORT_PRIVATE JSValue setCannotUseOSRExitFuzzing(ExecState*);
 JS_EXPORT_PRIVATE JSValue optimizeNextInvocation(ExecState*);
 
 JS_EXPORT_PRIVATE unsigned numberOfExceptionFuzzChecks();