Changeset 34842 in webkit for trunk/JavaScriptCore/VM/Machine.cpp


Ignore:
Timestamp:
Jun 27, 2008, 9:02:03 PM (17 years ago)
Author:
[email protected]
Message:

Bug 18626: SQUIRRELFISH: support the "slow script" dialog <https://bugs.webkit.org/show_bug.cgi?id=18626>
<rdar://problem/5973931> Slow script dialog needs to be reimplemented for squirrelfish

Reviewed by Sam

Adds support for the slow script dialog in squirrelfish. This requires the addition
of three new op codes, op_loop, op_loop_if_true, and op_loop_if_less which have the
same behaviour as their simple jump equivalents but have an additional time out check.

Additional assertions were added to other jump instructions to prevent accidentally
creating loops with jump types that do not support time out checks.

Sunspider does not report a regression, however this appears very sensitive to code
layout and hardware, so i would expect up to a 1% regression on other systems.

Part of this required moving the old timeout logic from JSGlobalObject and into Machine
which is the cause of a number of the larger diff blocks.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/VM/Machine.cpp

    r34838 r34842  
    5050#include "RegExpObject.h"
    5151
     52#if HAVE(SYS_TIME_H)
     53#include <sys/time.h>
     54#endif
     55
     56#if PLATFORM(WIN_OS)
     57#include <windows.h>
     58#endif
     59
     60#if PLATFORM(QT)
     61#include <QDateTime>
     62#endif
     63
    5264using namespace std;
    5365
    5466namespace KJS {
     67
     68// Default number of ticks before a timeout check should be done.
     69static const int initialTickCountThreshold = 255;
     70
     71// Preferred number of milliseconds between each timeout check
     72static const int preferredScriptCheckTimeInterval = 1000;
    5573
    5674#if HAVE(COMPUTED_GOTO)
     
    453471Machine::Machine()
    454472    : m_reentryDepth(0)
     473    , m_timeoutTime(0)
     474    , m_timeAtLastCheckTimeout(0)
     475    , m_timeExecuting(0)
     476    , m_timeoutCheckCount(0)
     477    , m_ticksUntilNextTimeoutCheck(initialTickCountThreshold)
    455478{
    456479    privateExecute(InitializeAndReturn);
     
    592615            exception->put(exec, Identifier(exec, "sourceURL"), jsOwnedString(exec, codeBlock->ownerNode->sourceURL()));
    593616        }
     617
     618        if (exception->isWatchdogException()) {
     619            while (unwindCallFrame(exec, exceptionValue, registerBase, vPC, codeBlock, k, scopeChain, r)) {
     620                 // Don't need handler checks or anything, we just want to unroll all the JS callframes possible.
     621            }
     622            return 0;
     623        }
    594624    }
    595625
     
    848878}
    849879
     880void Machine::resetTimeoutCheck()
     881{
     882    m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
     883    m_timeAtLastCheckTimeout = 0;
     884    m_timeExecuting = 0;
     885}
     886
     887// Returns the current time in milliseconds
     888// It doesn't matter what "current time" is here, just as long as
     889// it's possible to measure the time difference correctly.
     890// In an ideal world this would be in DateMath or some such, but unfortunately
     891// that's a regression.
     892static inline unsigned getCurrentTime()
     893{
     894#if HAVE(SYS_TIME_H)
     895    struct timeval tv;
     896    gettimeofday(&tv, 0);
     897    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
     898#elif PLATFORM(QT)
     899    QDateTime t = QDateTime::currentDateTime();
     900    return t.toTime_t() * 1000 + t.time().msec();
     901#elif PLATFORM(WIN_OS)
     902    return timeGetTime();
     903#else
     904#error Platform does not have getCurrentTime function
     905#endif
     906}
     907
     908// We have to return a JSValue here, gcc seems to produce worse code if
     909// we attempt to return a bool
     910ALWAYS_INLINE JSValue* Machine::checkTimeout(JSGlobalObject* globalObject)
     911{
     912    unsigned currentTime = getCurrentTime();
     913   
     914    if (!m_timeAtLastCheckTimeout) {
     915        // Suspicious amount of looping in a script -- start timing it
     916        m_timeAtLastCheckTimeout = currentTime;
     917        return 0;
     918    }
     919   
     920    unsigned timeDiff = currentTime - m_timeAtLastCheckTimeout;
     921   
     922    if (timeDiff == 0)
     923        timeDiff = 1;
     924   
     925    m_timeExecuting += timeDiff;
     926    m_timeAtLastCheckTimeout = currentTime;
     927   
     928    // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in
     929    // preferredScriptCheckTimeInterval
     930    m_ticksUntilNextTimeoutCheck = static_cast<unsigned>((static_cast<float>(preferredScriptCheckTimeInterval) / timeDiff) * m_ticksUntilNextTimeoutCheck);
     931    // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the
     932    // preferred script check time interval.
     933    if (m_ticksUntilNextTimeoutCheck == 0)
     934        m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
     935   
     936    if (m_timeoutTime && m_timeExecuting > m_timeoutTime) {
     937        if (globalObject->shouldInterruptScript())
     938            return jsNull(); // Appeasing GCC, all we need is a non-null js value.
     939       
     940        resetTimeoutCheck();
     941    }
     942   
     943    return 0;
     944}
     945   
    850946JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFile* registerFile, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue** exception)
    851947{
     
    875971    JSValue** k = codeBlock->jsValues.data();
    876972    Profiler** enabledProfilerReference = Profiler::enabledProfilerReference();
    877    
     973    unsigned tickCount = m_ticksUntilNextTimeoutCheck + 1;
     974
    878975#define VM_CHECK_EXCEPTION() \
    879976     do { \
     
    888985#endif
    889986
     987#define CHECK_FOR_TIMEOUT() \
     988    if (!--tickCount) { \
     989        if ((exceptionValue = checkTimeout(exec->dynamicGlobalObject()))) \
     990            goto vm_throw; \
     991        tickCount = m_ticksUntilNextTimeoutCheck; \
     992    }
     993   
    890994#if HAVE(COMPUTED_GOTO)
    891995    #define NEXT_OPCODE goto *vPC->u.opcode
     
    18441948        NEXT_OPCODE;
    18451949    }
    1846     BEGIN_OPCODE(op_jmp) {
    1847         /* jmp target(offset)
    1848 
     1950    BEGIN_OPCODE(op_loop) {
     1951        /* loop target(offset)
     1952         
    18491953           Jumps unconditionally to offset target from the current
    18501954           instruction.
    1851         */
     1955
     1956           Additionally this loop instruction may terminate JS execution is
     1957           the JS timeout is reached.
     1958         */
    18521959#if DUMP_OPCODE_STATS
    18531960        OpcodeStats::resetLastInstruction();
    18541961#endif
    18551962        int target = (++vPC)->u.operand;
    1856 
     1963        CHECK_FOR_TIMEOUT();
    18571964        vPC += target;
     1965        NEXT_OPCODE;
     1966    }
     1967    BEGIN_OPCODE(op_jmp) {
     1968        /* jmp target(offset)
     1969
     1970           Jumps unconditionally to offset target from the current
     1971           instruction.
     1972        */
     1973#if DUMP_OPCODE_STATS
     1974        OpcodeStats::resetLastInstruction();
     1975#endif
     1976        int target = (++vPC)->u.operand;
     1977
     1978        vPC += target;
     1979        NEXT_OPCODE;
     1980    }
     1981    BEGIN_OPCODE(op_loop_if_true) {
     1982        /* loop_if_true cond(r) target(offset)
     1983         
     1984           Jumps to offset target from the current instruction, if and
     1985           only if register cond converts to boolean as true.
     1986
     1987           Additionally this loop instruction may terminate JS execution is
     1988           the JS timeout is reached.
     1989         */
     1990        int cond = (++vPC)->u.operand;
     1991        int target = (++vPC)->u.operand;
     1992        if (r[cond].u.jsValue->toBoolean(exec)) {
     1993            vPC += target;
     1994            CHECK_FOR_TIMEOUT();
     1995            NEXT_OPCODE;
     1996        }
     1997       
     1998        ++vPC;
    18581999        NEXT_OPCODE;
    18592000    }
     
    18872028        }
    18882029
     2030        ++vPC;
     2031        NEXT_OPCODE;
     2032    }
     2033    BEGIN_OPCODE(op_loop_if_less) {
     2034        /* loop_if_less src1(r) src2(r) target(offset)
     2035
     2036           Checks whether register src1 is less than register src2, as
     2037           with the ECMAScript '<' operator, and then jumps to offset
     2038           target from the current instruction, if and only if the
     2039           result of the comparison is true.
     2040
     2041           Additionally this loop instruction may terminate JS execution is
     2042           the JS timeout is reached.
     2043         */
     2044        JSValue* src1 = r[(++vPC)->u.operand].u.jsValue;
     2045        JSValue* src2 = r[(++vPC)->u.operand].u.jsValue;
     2046        int target = (++vPC)->u.operand;
     2047       
     2048        bool result = jsLess(exec, src1, src2);
     2049        VM_CHECK_EXCEPTION();
     2050       
     2051        if (result) {
     2052            vPC += target;
     2053            CHECK_FOR_TIMEOUT();
     2054            NEXT_OPCODE;
     2055        }
     2056       
    18892057        ++vPC;
    18902058        NEXT_OPCODE;
     
    22852453        JSPropertyNameIterator* it = r[iter].u.jsPropertyNameIterator;
    22862454        if (JSValue* temp = it->next(exec)) {
     2455            CHECK_FOR_TIMEOUT();
    22872456            r[dst].u.jsValue = temp;
    22882457            vPC += target;
     
    24762645    vm_throw: {
    24772646        exec->clearException();
     2647        if (!tickCount) {
     2648            // The exceptionValue is a lie! (GCC produces bad code for reasons I
     2649            // cannot fathom if we don't assign to the exceptionValue before branching)
     2650            exceptionValue = createInterruptedExecutionException(exec);
     2651        }
    24782652        handlerVPC = throwException(exec, exceptionValue, &registerBase, vPC, codeBlock, k, scopeChain, r);
    24792653        if (!handlerVPC) {
Note: See TracChangeset for help on using the changeset viewer.