Title: [149089] trunk/Source/_javascript_Core
Revision
149089
Author
[email protected]
Date
2013-04-24 19:59:51 -0700 (Wed, 24 Apr 2013)

Log Message

Add watchdog timer polling for the DFG.
https://bugs.webkit.org/show_bug.cgi?id=115134.

Reviewed by Geoffrey Garen.

The strategy is to add a speculation check to the DFG generated code to
test if the watchdog timer has fired or not. If the watchdog timer has
fired, the generated code will do an OSR exit to the baseline JIT, and
let it handle servicing the watchdog timer.

If the watchdog is not enabled, this speculation check will not be
emitted.

* API/tests/testapi.c:
(currentCPUTime_callAsFunction):
(extendTerminateCallback):
(main):
- removed try/catch statements so that we can test the watchdog on the DFG.
- added JS bindings to a native currentCPUTime() function so that the timeout
  tests can be more accurate.
- also shortened the time values so that the tests can complete sooner.

* bytecode/ExitKind.h:
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/Watchdog.cpp:
(JSC::Watchdog::setTimeLimit):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/API/tests/testapi.c (149088 => 149089)


--- trunk/Source/_javascript_Core/API/tests/testapi.c	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/API/tests/testapi.c	2013-04-25 02:59:51 UTC (rev 149089)
@@ -1068,6 +1068,18 @@
     return time;
 }
 
+static JSValueRef currentCPUTime_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    UNUSED_PARAM(functionObject);
+    UNUSED_PARAM(thisObject);
+    UNUSED_PARAM(argumentCount);
+    UNUSED_PARAM(arguments);
+    UNUSED_PARAM(exception);
+
+    ASSERT(JSContextGetGlobalContext(ctx) == context);
+    return JSValueMakeNumber(ctx, currentCPUTime());
+}
+
 bool shouldTerminateCallbackWasCalled = false;
 static bool shouldTerminateCallback(JSContextRef ctx, void* context)
 {
@@ -1093,7 +1105,7 @@
     extendTerminateCallbackCalled++;
     if (extendTerminateCallbackCalled == 1) {
         JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
-        JSContextGroupSetExecutionTimeLimit(contextGroup, 2, extendTerminateCallback, 0);
+        JSContextGroupSetExecutionTimeLimit(contextGroup, .200f, extendTerminateCallback, 0);
         return false;
     }
     return true;
@@ -1749,30 +1761,63 @@
     }
 
 #if PLATFORM(MAC) || PLATFORM(IOS)
+    JSStringRef currentCPUTimeStr = JSStringCreateWithUTF8CString("currentCPUTime");
+    JSObjectRef currentCPUTimeFunction = JSObjectMakeFunctionWithCallback(context, currentCPUTimeStr, currentCPUTime_callAsFunction);
+    JSObjectSetProperty(context, globalObject, currentCPUTimeStr, currentCPUTimeFunction, kJSPropertyAttributeNone, NULL); 
+    JSStringRelease(currentCPUTimeStr);
+
     /* Test script timeout: */
-    JSContextGroupSetExecutionTimeLimit(contextGroup, 1, shouldTerminateCallback, 0);
+    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, shouldTerminateCallback, 0);
     {
-        const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
         JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
         double startTime;
         double endTime;
         exception = NULL;
+        shouldTerminateCallbackWasCalled = false;
         startTime = currentCPUTime();
         v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
         endTime = currentCPUTime();
 
-        if (((endTime - startTime) < 2) && shouldTerminateCallbackWasCalled)
+        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
             printf("PASS: script timed out as expected.\n");
         else {
-            if (!((endTime - startTime) < 2))
+            if (!((endTime - startTime) < .150f))
                 printf("FAIL: script did not timed out as expected.\n");
             if (!shouldTerminateCallbackWasCalled)
                 printf("FAIL: script timeout callback was not called.\n");
             failed = true;
         }
 
+        if (!exception) {
+            printf("FAIL: TerminationExecutionException was not thrown.\n");
+            failed = true;
+        }
+    }
+
+    /* Test the script timeout's TerminationExecutionException should NOT be catchable: */
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, shouldTerminateCallback, 0);
+    {
+        const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
+        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+        double startTime;
+        double endTime;
+        exception = NULL;
+        shouldTerminateCallbackWasCalled = false;
+        startTime = currentCPUTime();
+        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+        endTime = currentCPUTime();
+
+        if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
+            if (!((endTime - startTime) < .150f))
+                printf("FAIL: script did not timed out as expected.\n");
+            if (!shouldTerminateCallbackWasCalled)
+                printf("FAIL: script timeout callback was not called.\n");
+            failed = true;
+        }
+
         if (exception)
-            printf("PASS: TerminationExecutionException was not catchable.\n");
+            printf("PASS: TerminationExecutionException was not catchable as expected.\n");
         else {
             printf("FAIL: TerminationExecutionException was caught.\n");
             failed = true;
@@ -1780,9 +1825,9 @@
     }
 
     /* Test script timeout cancellation: */
-    JSContextGroupSetExecutionTimeLimit(contextGroup, 1, cancelTerminateCallback, 0);
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, cancelTerminateCallback, 0);
     {
-        const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
         JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
         double startTime;
         double endTime;
@@ -1791,10 +1836,10 @@
         v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
         endTime = currentCPUTime();
 
-        if (((endTime - startTime) >= 2) && cancelTerminateCallbackWasCalled && !exception)
+        if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
             printf("PASS: script timeout was cancelled as expected.\n");
         else {
-            if (((endTime - startTime) < 2) || exception)
+            if (((endTime - startTime) < .150) || exception)
                 printf("FAIL: script timeout was not cancelled.\n");
             if (!cancelTerminateCallbackWasCalled)
                 printf("FAIL: script timeout callback was not called.\n");
@@ -1803,9 +1848,9 @@
     }
 
     /* Test script timeout extension: */
-    JSContextGroupSetExecutionTimeLimit(contextGroup, 1, extendTerminateCallback, 0);
+    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.100f, extendTerminateCallback, 0);
     {
-        const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
         JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
         double startTime;
         double endTime;
@@ -1816,12 +1861,12 @@
         endTime = currentCPUTime();
         deltaTime = endTime - startTime;
 
-        if ((deltaTime >= 3) && (deltaTime < 5) && (extendTerminateCallbackCalled == 2) && exception)
+        if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
             printf("PASS: script timeout was extended as expected.\n");
         else {
-            if (deltaTime < 2)
+            if (deltaTime < .200f)
                 printf("FAIL: script timeout was not extended as expected.\n");
-            else if (deltaTime >= 5)
+            else if (deltaTime >= .500f)
                 printf("FAIL: script did not timeout.\n");
 
             if (extendTerminateCallbackCalled < 1)
@@ -1830,7 +1875,7 @@
                 printf("FAIL: script timeout callback was not called after timeout extension.\n");
 
             if (!exception)
-                printf("FAIL: TerminationExecutionException was caught during timeout extension test.\n");
+                printf("FAIL: TerminationExecutionException was not thrown during timeout extension test.\n");
 
             failed = true;
         }

Modified: trunk/Source/_javascript_Core/ChangeLog (149088 => 149089)


--- trunk/Source/_javascript_Core/ChangeLog	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-04-25 02:59:51 UTC (rev 149089)
@@ -1,3 +1,44 @@
+2013-04-24  Mark Lam  <[email protected]>
+
+        Add watchdog timer polling for the DFG.
+        https://bugs.webkit.org/show_bug.cgi?id=115134.
+
+        Reviewed by Geoffrey Garen.
+
+        The strategy is to add a speculation check to the DFG generated code to
+        test if the watchdog timer has fired or not. If the watchdog timer has
+        fired, the generated code will do an OSR exit to the baseline JIT, and
+        let it handle servicing the watchdog timer.
+
+        If the watchdog is not enabled, this speculation check will not be
+        emitted.
+
+        * API/tests/testapi.c:
+        (currentCPUTime_callAsFunction):
+        (extendTerminateCallback):
+        (main):
+        - removed try/catch statements so that we can test the watchdog on the DFG.
+        - added JS bindings to a native currentCPUTime() function so that the timeout
+          tests can be more accurate.
+        - also shortened the time values so that the tests can complete sooner.
+
+        * bytecode/ExitKind.h:
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * runtime/Watchdog.cpp:
+        (JSC::Watchdog::setTimeLimit):
+
 2013-04-24  Filip Pizlo  <[email protected]>
 
         Special thunks for math functions should work on ARMv7

Modified: trunk/Source/_javascript_Core/bytecode/ExitKind.h (149088 => 149089)


--- trunk/Source/_javascript_Core/bytecode/ExitKind.h	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/bytecode/ExitKind.h	2013-04-25 02:59:51 UTC (rev 149089)
@@ -46,7 +46,8 @@
     ArgumentsEscaped, // We exited because arguments escaped but we didn't expect them to.
     NotStringObject, // We exited because we shouldn't have attempted to optimize string object access.
     Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME.
-    UncountableWatchpoint // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves.
+    UncountableWatchpoint, // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves.
+    WatchdogTimerFired // We exited because we need to service the watchdog timer.
 };
 
 const char* exitKindToString(ExitKind);

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -1538,6 +1538,10 @@
         m_isValid = false;
         break;
             
+    case CheckWatchdogTimer:
+        node->setCanExit(true);
+        break;
+            
     case Phantom:
     case InlineStart:
     case Nop:

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -3285,9 +3285,13 @@
             if (!m_inlineStackTop->m_caller)
                 m_currentBlock->isOSRTarget = true;
 
-            // Emit a phantom node to ensure that there is a placeholder node for this bytecode
-            // op.
-            addToGraph(Phantom);
+            if (m_vm->watchdog.isEnabled())
+                addToGraph(CheckWatchdogTimer);
+            else {
+                // Emit a phantom node to ensure that there is a placeholder
+                // node for this bytecode op.
+                addToGraph(Phantom);
+            }
             
             NEXT_OPCODE(op_loop_hint);
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -859,6 +859,7 @@
         case GarbageValue:
         case CountExecution:
         case ForceOSRExit:
+        case CheckWatchdogTimer:
             break;
 #else
         default:

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2013-04-25 02:59:51 UTC (rev 149089)
@@ -264,7 +264,11 @@
     /* This is a pseudo-terminal. It means that execution should fall out of DFG at */\
     /* this point, but execution does continue in the basic block - just in a */\
     /* different compiler. */\
-    macro(ForceOSRExit, NodeMustGenerate)
+    macro(ForceOSRExit, NodeMustGenerate) \
+    \
+    /* Checks the watchdog timer. If the timer has fired, we OSR exit to the */ \
+    /* baseline JIT to redo the watchdog timer check, and service the timer. */ \
+    macro(CheckWatchdogTimer, NodeMustGenerate) \
 
 // This enum generates a monotonically increasing id for all Node types,
 // and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask).

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -526,6 +526,7 @@
         case Phantom:
         case PutGlobalVar:
         case PutGlobalVarCheck:
+        case CheckWatchdogTimer:
             break;
             
         // These gets ignored because it doesn't do anything.

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -4926,6 +4926,14 @@
         break;
     }
 
+    case CheckWatchdogTimer:
+        speculationCheck(
+            WatchdogTimerFired, JSValueRegs(), 0,
+            m_jit.branchTest8(
+                JITCompiler::NonZero,
+                JITCompiler::AbsoluteAddress(m_jit.vm()->watchdog.timerDidFireAddress())));
+        break;
+
     case CountExecution:
         m_jit.add64(TrustedImm32(1), MacroAssembler::AbsoluteAddress(node->executionCounter()->address()));
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -4770,6 +4770,14 @@
         break;
     }
 
+    case CheckWatchdogTimer:
+        speculationCheck(
+            WatchdogTimerFired, JSValueRegs(), 0,
+            m_jit.branchTest8(
+                JITCompiler::NonZero,
+                JITCompiler::AbsoluteAddress(m_jit.vm()->watchdog.timerDidFireAddress())));
+        break;
+
     case Phantom:
         DFG_NODE_DO_TO_CHILDREN(m_jit.graph(), node, speculate);
         noResult(node);

Modified: trunk/Source/_javascript_Core/runtime/Watchdog.cpp (149088 => 149089)


--- trunk/Source/_javascript_Core/runtime/Watchdog.cpp	2013-04-25 02:44:19 UTC (rev 149088)
+++ trunk/Source/_javascript_Core/runtime/Watchdog.cpp	2013-04-25 02:59:51 UTC (rev 149089)
@@ -79,11 +79,8 @@
     // timeout value, then any existing JITted code will have the appropriate
     // polling checks. Hence, there is no need to re-do this flushing.
     if (!wasEnabled) {
-        // For now, we only support this feature when the DFG JIT is disabled.
-        Options::useDFGJIT() = false;
-
-        // And if we've previously compiled any functions, we need to deopt
-        // them because they don't habe the needed polling checks yet.
+        // And if we've previously compiled any functions, we need to revert
+        // them because they don't have the needed polling checks yet.
         vm.releaseExecutableMemory();
     }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to