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