Changeset 204162 in webkit


Ignore:
Timestamp:
Aug 4, 2016, 11:46:55 PM (9 years ago)
Author:
sbarati@apple.com
Message:

Restore CodeBlock jettison code to jettison when a CodeBlock has been alive for a long time
https://bugs.webkit.org/show_bug.cgi?id=151241

Reviewed by Benjamin Poulain.

Source/JavaScriptCore:

This patch rolls back in the jettisoning policy from https://bugs.webkit.org/show_bug.cgi?id=149727.
We can now jettison a CodeBlock when it has been alive for a long time
and is only pointed to by its owner executable. I haven't been able to get this
patch to crash on anything it used to crash on, so I suspect we've fixed the bugs that
were causing this before. I've also added some stress options for this feature that
will cause us to either eagerly old-age jettison or to old-age jettison whenever it's legal.
These options helped me find a bug where we would ask an Executable to create a CodeBlock,
and then the Executable would do some other allocations, causing a GC, immediately causing
the CodeBlock to jettison. There is a small chance that this was the bug we were seeing before,
however, it's unlikely given that the previous timing metrics require at least 5 second between
compiling to jettisoning.

This patch also enables the stress options for various modes
of JSC stress tests.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::shouldJettisonDueToWeakReference):
(JSC::timeToLive):
(JSC::CodeBlock::shouldJettisonDueToOldAge):

  • interpreter/CallFrame.h:

(JSC::ExecState::callee):
(JSC::ExecState::unsafeCallee):
(JSC::ExecState::codeBlock):
(JSC::ExecState::addressOfCodeBlock):
(JSC::ExecState::unsafeCodeBlock):
(JSC::ExecState::scope):

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::prepareForRepeatCall):

  • jit/JITOperations.cpp:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::setUpCall):

  • runtime/Executable.cpp:

(JSC::ScriptExecutable::installCode):
(JSC::setupJIT):
(JSC::ScriptExecutable::prepareForExecutionImpl):

  • runtime/Executable.h:

(JSC::ScriptExecutable::prepareForExecution):

  • runtime/Options.h:

Tools:

  • Scripts/run-jsc-stress-tests:
Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r204160 r204162  
     12016-08-04  Saam Barati  <sbarati@apple.com>
     2
     3        Restore CodeBlock jettison code to jettison when a CodeBlock has been alive for a long time
     4        https://bugs.webkit.org/show_bug.cgi?id=151241
     5
     6        Reviewed by Benjamin Poulain.
     7
     8        This patch rolls back in the jettisoning policy from https://bugs.webkit.org/show_bug.cgi?id=149727.
     9        We can now jettison a CodeBlock when it has been alive for a long time
     10        and is only pointed to by its owner executable. I haven't been able to get this
     11        patch to crash on anything it used to crash on, so I suspect we've fixed the bugs that
     12        were causing this before. I've also added some stress options for this feature that
     13        will cause us to either eagerly old-age jettison or to old-age jettison whenever it's legal.
     14        These options helped me find a bug where we would ask an Executable to create a CodeBlock,
     15        and then the Executable would do some other allocations, causing a GC, immediately causing
     16        the CodeBlock to jettison. There is a small chance that this was the bug we were seeing before,
     17        however, it's unlikely given that the previous timing metrics require at least 5 second between
     18        compiling to jettisoning.
     19
     20        This patch also enables the stress options for various modes
     21        of JSC stress tests.
     22
     23        * bytecode/CodeBlock.cpp:
     24        (JSC::CodeBlock::shouldJettisonDueToWeakReference):
     25        (JSC::timeToLive):
     26        (JSC::CodeBlock::shouldJettisonDueToOldAge):
     27        * interpreter/CallFrame.h:
     28        (JSC::ExecState::callee):
     29        (JSC::ExecState::unsafeCallee):
     30        (JSC::ExecState::codeBlock):
     31        (JSC::ExecState::addressOfCodeBlock):
     32        (JSC::ExecState::unsafeCodeBlock):
     33        (JSC::ExecState::scope):
     34        * interpreter/Interpreter.cpp:
     35        (JSC::Interpreter::execute):
     36        (JSC::Interpreter::executeCall):
     37        (JSC::Interpreter::executeConstruct):
     38        (JSC::Interpreter::prepareForRepeatCall):
     39        * jit/JITOperations.cpp:
     40        * llint/LLIntSlowPaths.cpp:
     41        (JSC::LLInt::setUpCall):
     42        * runtime/Executable.cpp:
     43        (JSC::ScriptExecutable::installCode):
     44        (JSC::setupJIT):
     45        (JSC::ScriptExecutable::prepareForExecutionImpl):
     46        * runtime/Executable.h:
     47        (JSC::ScriptExecutable::prepareForExecution):
     48        * runtime/Options.h:
     49
    1502016-08-04  Yusuke Suzuki  <utatane.tea@gmail.com>
    251
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r203979 r204162  
    26522652}
    26532653
     2654static std::chrono::milliseconds timeToLive(JITCode::JITType jitType)
     2655{
     2656    if (UNLIKELY(Options::useEagerCodeBlockJettisonTiming())) {
     2657        switch (jitType) {
     2658        case JITCode::InterpreterThunk:
     2659            return std::chrono::milliseconds(10);
     2660        case JITCode::BaselineJIT:
     2661            return std::chrono::milliseconds(10 + 20);
     2662        case JITCode::DFGJIT:
     2663            return std::chrono::milliseconds(40);
     2664        case JITCode::FTLJIT:
     2665            return std::chrono::milliseconds(120);
     2666        default:
     2667            return std::chrono::milliseconds::max();
     2668        }
     2669    }
     2670
     2671    switch (jitType) {
     2672    case JITCode::InterpreterThunk:
     2673        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(5));
     2674    case JITCode::BaselineJIT:
     2675        // Effectively 10 additional seconds, since BaselineJIT and
     2676        // InterpreterThunk share a CodeBlock.
     2677        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(5 + 10));
     2678    case JITCode::DFGJIT:
     2679        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(20));
     2680    case JITCode::FTLJIT:
     2681        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(60));
     2682    default:
     2683        return std::chrono::milliseconds::max();
     2684    }
     2685}
     2686
    26542687bool CodeBlock::shouldJettisonDueToOldAge()
    26552688{
    2656     return false;
     2689    if (Heap::isMarked(this))
     2690        return false;
     2691
     2692    if (UNLIKELY(Options::forceCodeBlockToJettisonDueToOldAge()))
     2693        return true;
     2694   
     2695    if (timeSinceCreation() < timeToLive(jitType()))
     2696        return false;
     2697   
     2698    return true;
    26572699}
    26582700
  • trunk/Source/JavaScriptCore/interpreter/CallFrame.h

    r203081 r204162  
    9191        SUPPRESS_ASAN JSValue unsafeCallee() const { return this[CallFrameSlot::callee].asanUnsafeJSValue(); }
    9292        CodeBlock* codeBlock() const { return this[CallFrameSlot::codeBlock].Register::codeBlock(); }
     93        CodeBlock** addressOfCodeBlock() const { return bitwise_cast<CodeBlock**>(this + CallFrameSlot::codeBlock); }
    9394        SUPPRESS_ASAN CodeBlock* unsafeCodeBlock() const { return this[CallFrameSlot::codeBlock].Register::asanUnsafeCodeBlock(); }
    9495        JSScope* scope(int scopeRegisterOffset) const
  • trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r203790 r204162  
    945945        return checkedReturn(callFrame->vm().throwException(callFrame, error));
    946946
    947     if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall))
    948         return checkedReturn(callFrame->vm().throwException(callFrame, error));
    949 
    950     ProgramCodeBlock* codeBlock = program->codeBlock();
     947    ProgramCodeBlock* codeBlock;
     948    {
     949        CodeBlock* tempCodeBlock;
     950        if (JSObject* error = program->prepareForExecution<ProgramExecutable>(callFrame, nullptr, scope, CodeForCall, tempCodeBlock))
     951            return checkedReturn(callFrame->vm().throwException(callFrame, error));
     952        codeBlock = jsCast<ProgramCodeBlock*>(tempCodeBlock);
     953    }
    951954
    952955    if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
     
    996999    if (isJSCall) {
    9971000        // Compile the callee:
    998         JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall);
    999         if (UNLIKELY(!!compileError)) {
     1001        JSObject* compileError = callData.js.functionExecutable->prepareForExecution<FunctionExecutable>(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall, newCodeBlock);
     1002        if (UNLIKELY(!!compileError))
    10001003            return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
    1001         }
    1002         newCodeBlock = callData.js.functionExecutable->codeBlockForCall();
     1004
    10031005        ASSERT(!!newCodeBlock);
    10041006        newCodeBlock->m_shouldAlwaysBeInlined = false;
     
    10581060    if (isJSConstruct) {
    10591061        // Compile the callee:
    1060         JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct);
    1061         if (UNLIKELY(!!compileError)) {
     1062        JSObject* compileError = constructData.js.functionExecutable->prepareForExecution<FunctionExecutable>(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct, newCodeBlock);
     1063        if (UNLIKELY(!!compileError))
    10621064            return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
    1063         }
    1064         newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct();
     1065
    10651066        ASSERT(!!newCodeBlock);
    10661067        newCodeBlock->m_shouldAlwaysBeInlined = false;
     
    11021103
    11031104    // Compile the callee:
    1104     JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall);
     1105    CodeBlock* newCodeBlock;
     1106    JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(callFrame, function, scope, CodeForCall, newCodeBlock);
    11051107    if (error) {
    11061108        callFrame->vm().throwException(callFrame, error);
    11071109        return CallFrameClosure();
    11081110    }
    1109     CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall();
    11101111    newCodeBlock->m_shouldAlwaysBeInlined = false;
    11111112
     
    11771178    }
    11781179
    1179     JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
    1180     if (UNLIKELY(!!compileError))
    1181         return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
    1182     EvalCodeBlock* codeBlock = eval->codeBlock();
     1180    EvalCodeBlock* codeBlock;
     1181    {
     1182        CodeBlock* tempCodeBlock;
     1183        JSObject* compileError = eval->prepareForExecution<EvalExecutable>(callFrame, nullptr, scope, CodeForCall, tempCodeBlock);
     1184        if (UNLIKELY(!!compileError))
     1185            return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
     1186        codeBlock = jsCast<EvalCodeBlock*>(tempCodeBlock);
     1187    }
    11831188
    11841189    // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval.
     
    12541259        return checkedReturn(throwStackOverflowError(callFrame));
    12551260
    1256     JSObject* compileError = executable->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
    1257     if (UNLIKELY(!!compileError))
    1258         return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
    1259     ModuleProgramCodeBlock* codeBlock = executable->codeBlock();
     1261    ModuleProgramCodeBlock* codeBlock;
     1262    {
     1263        CodeBlock* tempCodeBlock;
     1264        JSObject* compileError = executable->prepareForExecution<ModuleProgramExecutable>(callFrame, nullptr, scope, CodeForCall, tempCodeBlock);
     1265        if (UNLIKELY(!!compileError))
     1266            return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
     1267        codeBlock = jsCast<ModuleProgramCodeBlock*>(tempCodeBlock);
     1268    }
    12601269
    12611270    if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
  • trunk/Source/JavaScriptCore/jit/JITOperations.cpp

    r204010 r204162  
    901901        }
    902902
    903         JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, scope, kind);
     903        CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
     904        JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(execCallee, callee, scope, kind, *codeBlockSlot);
    904905        if (error) {
    905906            exec->vm().throwException(exec, error);
     
    908909                reinterpret_cast<void*>(KeepTheFrame));
    909910        }
    910         codeBlock = functionExecutable->codeBlockFor(kind);
     911        codeBlock = *codeBlockSlot;
    911912        ArityCheckMode arity;
    912913        if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs())
     
    955956            }
    956957
    957             JSObject* error = functionExecutable->prepareForExecution(execCallee, function, scope, kind);
     958            CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
     959            JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(execCallee, function, scope, kind, *codeBlockSlot);
    958960            if (error) {
    959961                exec->vm().throwException(exec, error);
  • trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp

    r203452 r204162  
    12671267            LLINT_CALL_THROW(exec, createNotAConstructorError(exec, callee));
    12681268
    1269         JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, scope, kind);
     1269        CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
     1270        JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(execCallee, callee, scope, kind, *codeBlockSlot);
    12701271        if (error)
    12711272            LLINT_CALL_THROW(exec, error);
    1272         codeBlock = functionExecutable->codeBlockFor(kind);
     1273        codeBlock = *codeBlockSlot;
    12731274        ASSERT(codeBlock);
    12741275        ArityCheckMode arity;
  • trunk/Source/JavaScriptCore/runtime/Executable.cpp

    r202734 r204162  
    178178    ASSERT(vm.heap.isDeferred());
    179179   
    180     CODEBLOCK_LOG_EVENT(genericCodeBlock, "installCode", ());
     180    if (genericCodeBlock)
     181        CODEBLOCK_LOG_EVENT(genericCodeBlock, "installCode", ());
    181182   
    182183    CodeBlock* oldCodeBlock = nullptr;
     
    400401
    401402JSObject* ScriptExecutable::prepareForExecutionImpl(
    402     ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind)
     403    ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock)
    403404{
    404405    VM& vm = exec->vm();
    405     DeferGC deferGC(vm.heap);
     406    DeferGCForAWhile deferGC(vm.heap);
    406407
    407408    if (vm.getAndClearFailNextNewCodeBlock())
     
    410411    JSObject* exception = 0;
    411412    CodeBlock* codeBlock = newCodeBlockFor(kind, function, scope, exception);
     413    resultCodeBlock = codeBlock;
    412414    if (!codeBlock) {
    413415        RELEASE_ASSERT(exception);
     
    424426   
    425427    installCode(*codeBlock->vm(), codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
    426     return 0;
     428    return nullptr;
    427429}
    428430
  • trunk/Source/JavaScriptCore/runtime/Executable.h

    r204137 r204162  
    368368    CodeBlock* newCodeBlockFor(CodeSpecializationKind, JSFunction*, JSScope*, JSObject*& exception);
    369369    CodeBlock* newReplacementCodeBlockFor(CodeSpecializationKind);
    370    
    371     JSObject* prepareForExecution(ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind)
    372     {
    373         if (hasJITCodeFor(kind))
    374             return 0;
    375         return prepareForExecutionImpl(exec, function, scope, kind);
    376     }
     370
     371    // This function has an interesting GC story. Callers of this function are asking us to create a CodeBlock
     372    // that is not jettisoned before this function returns. Callers are essentially asking for a strong reference
     373    // to the CodeBlock. Because the Executable may be allocating the CodeBlock, we require callers to pass in
     374    // their CodeBlock*& reference because it's safe for CodeBlock to be jettisoned if Executable is the only thing
     375    // to point to it. This forces callers to have a CodeBlock* in a register or on the stack that will be marked
     376    // by conservative GC if a GC happens after we create the CodeBlock.
     377    template <typename ExecutableType>
     378    JSObject* prepareForExecution(ExecState*, JSFunction*, JSScope*, CodeSpecializationKind, CodeBlock*& resultCodeBlock);
    377379
    378380    template <typename Functor> void forEachCodeBlock(Functor&&);
     
    380382private:
    381383    friend class ExecutableBase;
    382     JSObject* prepareForExecutionImpl(ExecState*, JSFunction*, JSScope*, CodeSpecializationKind);
     384    JSObject* prepareForExecutionImpl(ExecState*, JSFunction*, JSScope*, CodeSpecializationKind, CodeBlock*&);
    383385
    384386protected:
     
    740742#endif
    741743
     744template <typename ExecutableType>
     745JSObject* ScriptExecutable::prepareForExecution(ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock)
     746{
     747    if (hasJITCodeFor(kind)) {
     748        if (std::is_same<ExecutableType, EvalExecutable>::value)
     749            resultCodeBlock = jsCast<CodeBlock*>(jsCast<EvalExecutable*>(this)->codeBlock());
     750        else if (std::is_same<ExecutableType, ProgramExecutable>::value)
     751            resultCodeBlock = jsCast<CodeBlock*>(jsCast<ProgramExecutable*>(this)->codeBlock());
     752        else if (std::is_same<ExecutableType, ModuleProgramExecutable>::value)
     753            resultCodeBlock = jsCast<CodeBlock*>(jsCast<ModuleProgramExecutable*>(this)->codeBlock());
     754        else if (std::is_same<ExecutableType, FunctionExecutable>::value)
     755            resultCodeBlock = jsCast<CodeBlock*>(jsCast<FunctionExecutable*>(this)->codeBlockFor(kind));
     756        else
     757            RELEASE_ASSERT_NOT_REACHED();
     758        return nullptr;
     759    }
     760    return prepareForExecutionImpl(exec, function, scope, kind, resultCodeBlock);
     761}
     762
    742763} // namespace JSC
    743764
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r203329 r204162  
    323323    v(bool, recordGCPauseTimes, false, Normal, nullptr) \
    324324    v(bool, logHeapStatisticsAtExit, false, Normal, nullptr) \
     325    v(bool, forceCodeBlockToJettisonDueToOldAge, false, Normal, "If true, this means that anytime we can jettison a CodeBlock due to old age, we do.") \
     326    v(bool, useEagerCodeBlockJettisonTiming, false, Normal, "If true, the time slices for jettisoning a CodeBlock due to old age are shrunk significantly.") \
    325327    \
    326328    v(bool, useTypeProfiler, false, Normal, nullptr) \
  • trunk/Tools/ChangeLog

    r204156 r204162  
     12016-08-04  Saam Barati  <sbarati@apple.com>
     2
     3        Restore CodeBlock jettison code to jettison when a CodeBlock has been alive for a long time
     4        https://bugs.webkit.org/show_bug.cgi?id=151241
     5
     6        Reviewed by Benjamin Poulain.
     7
     8        * Scripts/run-jsc-stress-tests:
     9
    1102016-08-04  Dean Johnson  <dean_johnson@apple.com>
    211
  • trunk/Tools/Scripts/run-jsc-stress-tests

    r203625 r204162  
    420420# We force all tests to use a smaller (1.5M) stack so that stack overflow tests can run faster.
    421421BASE_OPTIONS = ["--useFTLJIT=false", "--useFunctionDotArguments=true", "--maxPerThreadStackUsage=1572864"]
    422 EAGER_OPTIONS = ["--thresholdForJITAfterWarmUp=10", "--thresholdForJITSoon=10", "--thresholdForOptimizeAfterWarmUp=20", "--thresholdForOptimizeAfterLongWarmUp=20", "--thresholdForOptimizeSoon=20", "--thresholdForFTLOptimizeAfterWarmUp=20", "--thresholdForFTLOptimizeSoon=20", "--maximumEvalCacheableSourceLength=150000"]
     422EAGER_OPTIONS = ["--thresholdForJITAfterWarmUp=10", "--thresholdForJITSoon=10", "--thresholdForOptimizeAfterWarmUp=20", "--thresholdForOptimizeAfterLongWarmUp=20", "--thresholdForOptimizeSoon=20", "--thresholdForFTLOptimizeAfterWarmUp=20", "--thresholdForFTLOptimizeSoon=20", "--maximumEvalCacheableSourceLength=150000", "--useEagerCodeBlockJettisonTiming=true"]
    423423NO_CJIT_OPTIONS = ["--useConcurrentJIT=false", "--thresholdForJITAfterWarmUp=100"]
    424424FTL_OPTIONS = ["--useFTLJIT=true"]
     
    877877
    878878def runDFGMaximalFlushPhase
    879     run("dfg-maximal-flush-validate-no-cjit", "--validateGraph=true", "--useMaximalFlushInsertionPhase=true", *NO_CJIT_OPTIONS)
     879    run("dfg-maximal-flush-validate-no-cjit", "--forceCodeBlockToJettisonDueToOldAge=true", "--validateGraph=true", "--useMaximalFlushInsertionPhase=true", *NO_CJIT_OPTIONS)
    880880end
    881881
Note: See TracChangeset for help on using the changeset viewer.