Changeset 211658 in webkit


Ignore:
Timestamp:
Feb 3, 2017 5:17:38 PM (7 years ago)
Author:
jfbastien@apple.com
Message:

OSR entry: delay outer-loop compilation when at inner-loop
https://bugs.webkit.org/show_bug.cgi?id=167149

Reviewed by Filip Pizlo.

r211224 and r211461 were reverted because they caused massive
kraken/ai-astar regressions. This patch instead does the
minimally-disruptive change to fix the original bug as described
below, but omits extra tuning and refactoring which I had
before. I'll commit tuning and refactoring separately, if this
sticks. This patch is therefore very minimal, and layers carefully
on top of the complex spaghetti-logic. The only change it makes is
that it uses triggers to indicate to outer loops that they should
compile, which fixes the immediate bug and seems roughly perf
neutral (maybe a small gain on kraken sometimes, other times a
small regression as would be expected from slightly compiling
later). As opposed to r211461 this patch doesn't unconditionally
unset the trigger because it prevents further DFG executions from
entering. It therefore makes the trigger a tri-state enum class:
don't trigger, compilation done, start compilation. Only "start
compilation" gets reset to "don't trigger". "Compilation done"
does not (unless there's a problem compiling, then it gets set
back to "don't trigger").

As of https://bugs.webkit.org/show_bug.cgi?id=155217 OSR
compilation can be kicked off for an entry into an outer-loop,
while executing an inner-loop. This is desirable because often the
codegen from an inner-entry isn't as good as the codegen from an
outer-entry, but execution from an inner-loop is often pretty hot
and likely to kick off compilation. This approach provided nice
speedups on Kraken because we'd select to enter to the outer-loop
very reliably, which reduces variability (the inner-loop was
selected roughly 1/5 times from my unscientific measurements).

When compilation starts we take a snapshot of the JSValues at the
current execution state using OSR's recovery mechanism. These
values are passed to the compiler and are used as way to perform
type profiling, and could be used to observe cell types as well as
to perform predictions such as through constant propagation.

It's therefore desired to enter from the outer-loop when we can,
but we need to be executing from that location to capture the
right JSValues, otherwise we're confusing the compiler and giving
it inaccurate JSValues which can lead it to predict the wrong
things, leading to suboptimal code or recompilation due to
misprediction, or in super-corner-cases a crash.

DFG tier-up was added here:
https://bugs.webkit.org/show_bug.cgi?id=112838

  • dfg/DFGJITCode.h:
  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::JITCompiler):

  • dfg/DFGOperations.cpp:
  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGToFTLForOSREntryDeferredCompilationCallback.cpp:

(JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::ToFTLForOSREntryDeferredCompilationCallback):
(JSC::DFG::Ref<ToFTLForOSREntryDeferredCompilationCallback>ToFTLForOSREntryDeferredCompilationCallback::create):
(JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::compilationDidBecomeReadyAsynchronously):
(JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::compilationDidComplete):

  • dfg/DFGToFTLForOSREntryDeferredCompilationCallback.h:
Location:
trunk/Source/JavaScriptCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r211642 r211658  
     12017-02-03  JF Bastien  <jfbastien@apple.com>
     2
     3        OSR entry: delay outer-loop compilation when at inner-loop
     4        https://bugs.webkit.org/show_bug.cgi?id=167149
     5
     6        Reviewed by Filip Pizlo.
     7
     8        r211224 and r211461 were reverted because they caused massive
     9        kraken/ai-astar regressions. This patch instead does the
     10        minimally-disruptive change to fix the original bug as described
     11        below, but omits extra tuning and refactoring which I had
     12        before. I'll commit tuning and refactoring separately, if this
     13        sticks. This patch is therefore very minimal, and layers carefully
     14        on top of the complex spaghetti-logic. The only change it makes is
     15        that it uses triggers to indicate to outer loops that they should
     16        compile, which fixes the immediate bug and seems roughly perf
     17        neutral (maybe a small gain on kraken sometimes, other times a
     18        small regression as would be expected from slightly compiling
     19        later). As opposed to r211461 this patch doesn't unconditionally
     20        unset the trigger because it prevents further DFG executions from
     21        entering. It therefore makes the trigger a tri-state enum class:
     22        don't trigger, compilation done, start compilation. Only "start
     23        compilation" gets reset to "don't trigger". "Compilation done"
     24        does not (unless there's a problem compiling, then it gets set
     25        back to "don't trigger").
     26
     27        As of https://bugs.webkit.org/show_bug.cgi?id=155217 OSR
     28        compilation can be kicked off for an entry into an outer-loop,
     29        while executing an inner-loop. This is desirable because often the
     30        codegen from an inner-entry isn't as good as the codegen from an
     31        outer-entry, but execution from an inner-loop is often pretty hot
     32        and likely to kick off compilation. This approach provided nice
     33        speedups on Kraken because we'd select to enter to the outer-loop
     34        very reliably, which reduces variability (the inner-loop was
     35        selected roughly 1/5 times from my unscientific measurements).
     36
     37        When compilation starts we take a snapshot of the JSValues at the
     38        current execution state using OSR's recovery mechanism. These
     39        values are passed to the compiler and are used as way to perform
     40        type profiling, and could be used to observe cell types as well as
     41        to perform predictions such as through constant propagation.
     42
     43        It's therefore desired to enter from the outer-loop when we can,
     44        but we need to be executing from that location to capture the
     45        right JSValues, otherwise we're confusing the compiler and giving
     46        it inaccurate JSValues which can lead it to predict the wrong
     47        things, leading to suboptimal code or recompilation due to
     48        misprediction, or in super-corner-cases a crash.
     49
     50        DFG tier-up was added here:
     51        https://bugs.webkit.org/show_bug.cgi?id=112838
     52
     53        * dfg/DFGJITCode.h:
     54        * dfg/DFGJITCompiler.cpp:
     55        (JSC::DFG::JITCompiler::JITCompiler):
     56        * dfg/DFGOperations.cpp:
     57        * dfg/DFGSpeculativeJIT64.cpp:
     58        (JSC::DFG::SpeculativeJIT::compile):
     59        * dfg/DFGToFTLForOSREntryDeferredCompilationCallback.cpp:
     60        (JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::ToFTLForOSREntryDeferredCompilationCallback):
     61        (JSC::DFG::Ref<ToFTLForOSREntryDeferredCompilationCallback>ToFTLForOSREntryDeferredCompilationCallback::create):
     62        (JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::compilationDidBecomeReadyAsynchronously):
     63        (JSC::DFG::ToFTLForOSREntryDeferredCompilationCallback::compilationDidComplete):
     64        * dfg/DFGToFTLForOSREntryDeferredCompilationCallback.h:
     65
    1662017-02-03  Saam Barati  <sbarati@apple.com>
    267
  • trunk/Source/JavaScriptCore/dfg/DFGJITCode.h

    r211245 r211658  
    152152    HashMap<unsigned, unsigned, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> bytecodeIndexToStreamIndex;
    153153
     154    enum class TriggerReason : uint8_t {
     155        DontTrigger,
     156        CompilationDone,
     157        StartCompilation,
     158    };
     159
    154160    // Map each bytecode of CheckTierUpAndOSREnter to its trigger forcing OSR Entry.
    155161    // This can never be modified after it has been initialized since the addresses of the triggers
    156162    // are used by the JIT.
    157     HashMap<unsigned, uint8_t> tierUpEntryTriggers;
     163    HashMap<unsigned, TriggerReason> tierUpEntryTriggers;
    158164
    159165    // Set of bytecode that were the target of a TierUp operation.
  • trunk/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp

    r211245 r211658  
    6161    m_jitCode->tierUpInLoopHierarchy = WTFMove(m_graph.m_plan.tierUpInLoopHierarchy);
    6262    for (unsigned tierUpBytecode : m_graph.m_plan.tierUpAndOSREnterBytecodes)
    63         m_jitCode->tierUpEntryTriggers.add(tierUpBytecode, 0);
     63        m_jitCode->tierUpEntryTriggers.add(tierUpBytecode, JITCode::TriggerReason::DontTrigger);
    6464#endif
    6565}
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r211548 r211658  
    23522352
    23532353    JITCode* jitCode = codeBlock->jitCode()->dfg();
     2354
     2355    // The following is only true for triggerTierUpNowInLoop, which can never
     2356    // be an OSR entry.
     2357    bool canOSRFromHere = originBytecodeIndex == osrEntryBytecodeIndex;
     2358
     2359    bool triggeredSlowPathToStartCompilation = false;
     2360    auto tierUpEntryTriggers = jitCode->tierUpEntryTriggers.find(originBytecodeIndex);
     2361    if (tierUpEntryTriggers != jitCode->tierUpEntryTriggers.end()) {
     2362        switch (tierUpEntryTriggers->value) {
     2363        case JITCode::TriggerReason::DontTrigger:
     2364            // The trigger isn't set, we entered because the counter reached its
     2365            // threshold.
     2366            break;
     2367
     2368        case JITCode::TriggerReason::CompilationDone:
     2369            // The trigger was set because compilation completed. Don't unset it
     2370            // so that further DFG executions OSR enters as well.
     2371            RELEASE_ASSERT(canOSRFromHere);
     2372            break;
     2373
     2374        case JITCode::TriggerReason::StartCompilation:
     2375            // We were asked to enter as soon as possible and start compiling an
     2376            // entry for the current bytecode location. Unset this trigger so we
     2377            // don't continually enter.
     2378            RELEASE_ASSERT(canOSRFromHere);
     2379            tierUpEntryTriggers->value = JITCode::TriggerReason::DontTrigger;
     2380            triggeredSlowPathToStartCompilation = true;
     2381            break;
     2382        }
     2383    }
     2384
    23542385    if (worklistState == Worklist::Compiling) {
    23552386        CODEBLOCK_LOG_EVENT(codeBlock, "delayFTLCompile", ("still compiling"));
     
    23682399
    23692400    // If we can OSR Enter, do it right away.
    2370     if (originBytecodeIndex == osrEntryBytecodeIndex) {
     2401    if (canOSRFromHere) {
    23712402        unsigned streamIndex = jitCode->bytecodeIndexToStreamIndex.get(originBytecodeIndex);
    23722403        if (CodeBlock* entryBlock = jitCode->osrEntryBlock()) {
     
    23822413    // - If we couldn't enter for a while, then trigger OSR entry.
    23832414
    2384     if (!shouldTriggerFTLCompile(codeBlock, jitCode))
     2415    if (!shouldTriggerFTLCompile(codeBlock, jitCode) && !triggeredSlowPathToStartCompilation)
    23852416        return nullptr;
    23862417
    2387     if (!jitCode->neverExecutedEntry) {
     2418    if (!jitCode->neverExecutedEntry && !triggeredSlowPathToStartCompilation) {
    23882419        triggerFTLReplacementCompile(vm, codeBlock, jitCode);
    23892420
     
    24252456        jitCode->clearOSREntryBlock();
    24262457        jitCode->osrEntryRetry = 0;
    2427         jitCode->tierUpEntryTriggers.set(osrEntryBytecode, 0);
     2458        jitCode->tierUpEntryTriggers.set(osrEntryBytecode, JITCode::TriggerReason::DontTrigger);
    24282459        jitCode->setOptimizationThresholdBasedOnCompilationResult(
    24292460            codeBlock, CompilationDeferred);
     
    24312462    }
    24322463
     2464    if (!canOSRFromHere) {
     2465        // We can't OSR from here, or even start a compilation because doing so
     2466        // calls jitCode->reconstruct which would get the wrong state.
     2467        if (Options::verboseOSR())
     2468            dataLog("Non-OSR-able bc#", originBytecodeIndex, " in ", *codeBlock, " setting parent loop bc#", osrEntryBytecodeIndex, "'s trigger and backing off.\n");
     2469        jitCode->tierUpEntryTriggers.set(osrEntryBytecodeIndex, JITCode::TriggerReason::StartCompilation);
     2470        jitCode->setOptimizationThresholdBasedOnCompilationResult(codeBlock, CompilationDeferred);
     2471        return nullptr;
     2472    }
     2473
    24332474    unsigned streamIndex = jitCode->bytecodeIndexToStreamIndex.get(osrEntryBytecodeIndex);
    2434     auto tierUpHierarchyEntry = jitCode->tierUpInLoopHierarchy.find(osrEntryBytecodeIndex);
    2435     if (tierUpHierarchyEntry != jitCode->tierUpInLoopHierarchy.end()) {
    2436         for (unsigned osrEntryCandidate : tierUpHierarchyEntry->value) {
    2437             if (jitCode->tierUpEntrySeen.contains(osrEntryCandidate)) {
    2438                 osrEntryBytecodeIndex = osrEntryCandidate;
    2439                 streamIndex = jitCode->bytecodeIndexToStreamIndex.get(osrEntryBytecodeIndex);
     2475
     2476    if (!triggeredSlowPathToStartCompilation) {
     2477        auto tierUpHierarchyEntry = jitCode->tierUpInLoopHierarchy.find(osrEntryBytecodeIndex);
     2478        if (tierUpHierarchyEntry != jitCode->tierUpInLoopHierarchy.end()) {
     2479            for (unsigned osrEntryCandidate : tierUpHierarchyEntry->value) {
     2480                if (jitCode->tierUpEntrySeen.contains(osrEntryCandidate)) {
     2481                    // Ask an enclosing loop to compile, instead of doing so here.
     2482                    if (Options::verboseOSR())
     2483                        dataLog("Inner-loop bc#", originBytecodeIndex, " in ", *codeBlock, " setting parent loop bc#", osrEntryCandidate, "'s trigger and backing off.\n");
     2484                    jitCode->tierUpEntryTriggers.set(osrEntryCandidate, JITCode::TriggerReason::StartCompilation);
     2485                    jitCode->setOptimizationThresholdBasedOnCompilationResult(codeBlock, CompilationDeferred);
     2486                    return nullptr;
     2487                }
    24402488            }
    24412489        }
     
    24462494    auto triggerIterator = jitCode->tierUpEntryTriggers.find(osrEntryBytecodeIndex);
    24472495    RELEASE_ASSERT(triggerIterator != jitCode->tierUpEntryTriggers.end());
    2448     uint8_t* triggerAddress = &(triggerIterator->value);
     2496    JITCode::TriggerReason* triggerAddress = &(triggerIterator->value);
    24492497
    24502498    Operands<JSValue> mustHandleValues;
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r211247 r211658  
    57825782        auto triggerIterator = m_jit.jitCode()->tierUpEntryTriggers.find(bytecodeIndex);
    57835783        DFG_ASSERT(m_jit.graph(), node, triggerIterator != m_jit.jitCode()->tierUpEntryTriggers.end());
    5784         uint8_t* forceEntryTrigger = &(m_jit.jitCode()->tierUpEntryTriggers.find(bytecodeIndex)->value);
     5784        JITCode::TriggerReason* forceEntryTrigger = &(m_jit.jitCode()->tierUpEntryTriggers.find(bytecodeIndex)->value);
     5785        static_assert(!static_cast<uint8_t>(JITCode::TriggerReason::DontTrigger), "the JIT code assumes non-zero means 'enter'");
     5786        static_assert(sizeof(JITCode::TriggerReason) == 1, "branchTest8 assumes this size");
    57855787
    57865788        MacroAssembler::Jump forceOSREntry = m_jit.branchTest8(MacroAssembler::NonZero, MacroAssembler::AbsoluteAddress(forceEntryTrigger));
  • trunk/Source/JavaScriptCore/dfg/DFGToFTLForOSREntryDeferredCompilationCallback.cpp

    r211245 r211658  
    3636namespace JSC { namespace DFG {
    3737
    38 ToFTLForOSREntryDeferredCompilationCallback::ToFTLForOSREntryDeferredCompilationCallback(uint8_t* forcedOSREntryTrigger)
     38ToFTLForOSREntryDeferredCompilationCallback::ToFTLForOSREntryDeferredCompilationCallback(JITCode::TriggerReason* forcedOSREntryTrigger)
    3939    : m_forcedOSREntryTrigger(forcedOSREntryTrigger)
    4040{
     
    4545}
    4646
    47 Ref<ToFTLForOSREntryDeferredCompilationCallback>ToFTLForOSREntryDeferredCompilationCallback::create(uint8_t* forcedOSREntryTrigger)
     47Ref<ToFTLForOSREntryDeferredCompilationCallback>ToFTLForOSREntryDeferredCompilationCallback::create(JITCode::TriggerReason* forcedOSREntryTrigger)
    4848{
    4949    return adoptRef(*new ToFTLForOSREntryDeferredCompilationCallback(forcedOSREntryTrigger));
     
    5959    }
    6060
    61     *m_forcedOSREntryTrigger = 1;
     61    *m_forcedOSREntryTrigger = JITCode::TriggerReason::CompilationDone;
    6262}
    6363
     
    7777        jitCode->setOSREntryBlock(*codeBlock->vm(), profiledDFGCodeBlock, codeBlock);
    7878        unsigned osrEntryBytecode = codeBlock->jitCode()->ftlForOSREntry()->bytecodeIndex();
    79         jitCode->tierUpEntryTriggers.set(osrEntryBytecode, 1);
     79        jitCode->tierUpEntryTriggers.set(osrEntryBytecode, JITCode::TriggerReason::CompilationDone);
    8080        break;
    8181    }
  • trunk/Source/JavaScriptCore/dfg/DFGToFTLForOSREntryDeferredCompilationCallback.h

    r211245 r211658  
    2828#if ENABLE(FTL_JIT)
    2929
     30#include "DFGJITCode.h"
    3031#include "DeferredCompilationCallback.h"
    3132#include <wtf/RefPtr.h>
     
    3940class ToFTLForOSREntryDeferredCompilationCallback : public DeferredCompilationCallback {
    4041protected:
    41     ToFTLForOSREntryDeferredCompilationCallback(uint8_t* forcedOSREntryTrigger);
     42    ToFTLForOSREntryDeferredCompilationCallback(JITCode::TriggerReason* forcedOSREntryTrigger);
    4243
    4344public:
    4445    virtual ~ToFTLForOSREntryDeferredCompilationCallback();
    4546
    46     static Ref<ToFTLForOSREntryDeferredCompilationCallback> create(uint8_t* forcedOSREntryTrigger);
     47    static Ref<ToFTLForOSREntryDeferredCompilationCallback> create(JITCode::TriggerReason* forcedOSREntryTrigger);
    4748   
    4849    virtual void compilationDidBecomeReadyAsynchronously(CodeBlock*, CodeBlock* profiledDFGCodeBlock);
     
    5051
    5152private:
    52     uint8_t* m_forcedOSREntryTrigger;
     53    JITCode::TriggerReason* m_forcedOSREntryTrigger;
    5354};
    5455
Note: See TracChangeset for help on using the changeset viewer.