Ignore:
Timestamp:
Aug 28, 2013, 9:03:05 PM (12 years ago)
Author:
[email protected]
Message:

CodeBlock compilation and installation should be simplified and rationalized
https://bugs.webkit.org/show_bug.cgi?id=120326

Reviewed by Oliver Hunt.

Previously Executable owned the code for generating JIT code; you always had
to go through Executable. But often you also had to go through CodeBlock,
because ScriptExecutable couldn't have virtual methods, but CodeBlock could.
So you'd ask CodeBlock to do something, which would dispatch through a
virtual method that would select the appropriate Executable subtype's method.
This all meant that the same code would often be duplicated, because most of
the work needed to compile something was identical regardless of code type.
But then we tried to fix this, by having templatized helpers in
ExecutionHarness.h and JITDriver.h. The result was that if you wanted to find
out what happened when you asked for something to be compiled, you'd go on a
wild ride that started with CodeBlock, touched upon Executable, and then
ricocheted into either ExecutionHarness or JITDriver (likely both).

Another awkwardness was that for concurrent compiles, the DFG::Worklist had
super-special inside knowledge of what JITStubs.cpp's cti_optimize would have
done once the compilation finished.

Also, most of the DFG JIT drivers assumed that they couldn't install the
JITCode into the CodeBlock directly - instead they would return it via a
reference, which happened to be a reference to the JITCode pointer in
Executable. This was super weird.

Finally, there was no notion of compiling code into a special CodeBlock that
wasn't used for handling calls into an Executable. I'd like this for FTL OSR
entry.

This patch solves these problems by reducing all of that complexity into just
three primitives:

  • Executable::newCodeBlock(). This gives you a new code block, either for call or for construct, and either to serve as the baseline code or the optimized code. The new code block is then owned by the caller; Executable doesn't register it anywhere. The new code block has no JITCode and isn't callable, but it has all of the bytecode.


  • CodeBlock::prepareForExecution(). This takes the CodeBlock's bytecode and produces a JITCode, and then installs the JITCode into the CodeBlock. This method takes a JITType, and always compiles with that JIT. If you ask for JITCode::InterpreterThunk then you'll get JITCode that just points to the LLInt entrypoints. Once this returns, it is possible to call into the CodeBlock if you do so manually - but the Executable still won't know about it so JS calls to that Executable will still be routed to whatever CodeBlock is associated with the Executable.


  • Executable::installCode(). This takes a CodeBlock and makes it the code-for- entry for that Executable. This involves unlinking the Executable's last CodeBlock, if there was one. This also tells the GC about any effect on memory usage and does a bunch of weird data structure rewiring, since Executable caches some of CodeBlock's fields for the benefit of virtual call fast paths.


This functionality is then wrapped around three convenience methods:

  • Executable::prepareForExecution(). If there is no code block for that Executable, then one is created (newCodeBlock()), compiled (CodeBlock::prepareForExecution()) and installed (installCode()).


  • CodeBlock::newReplacement(). Asks the Executable for a new CodeBlock that can serve as an optimized replacement of the current one.


  • CodeBlock::install(). Asks the Executable to install this code block.


This patch allows me to kill *a lot* of code and to remove a lot of
specializations for functions vs. not-functions, and a lot of places where we
pass around JITCode references and such. ExecutionHarness and JITDriver are
both gone. Overall this patch has more red than green.

It also allows me to work on FTL OSR entry and tier-up:

  • FTL tier-up: this will involve DFGOperations.cpp asking the DFG::Worklist to do some compilation, but it will require the DFG::Worklist to do something different than what JITStubs.cpp would want, once the compilation finishes. This patch introduces a callback mechanism for that purpose.


  • FTL OSR entry: this will involve creating a special auto-jettisoned CodeBlock that is used only for FTL OSR entry. The new set of primitives allows for this: Executable can vend you a fresh new CodeBlock, and you can ask that CodeBlock to compile itself with any JIT of your choosing. Or you can take that CodeBlock and compile it yourself. Previously the act of producing a CodeBlock-for-optimization and the act of compiling code for it were tightly coupled; now you can separate them and you can create such auto-jettisoned CodeBlocks that are used for a one-shot OSR entry.
  • CMakeLists.txt:
  • GNUmakefile.list.am:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Target.pri:
  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::prepareForExecution):
(JSC::CodeBlock::install):
(JSC::CodeBlock::newReplacement):
(JSC::FunctionCodeBlock::jettisonImpl):
(JSC::CodeBlock::setOptimizationThresholdBasedOnCompilationResult):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::hasBaselineJITProfiling):

  • bytecode/DeferredCompilationCallback.cpp: Added.

(JSC::DeferredCompilationCallback::DeferredCompilationCallback):
(JSC::DeferredCompilationCallback::~DeferredCompilationCallback):

  • bytecode/DeferredCompilationCallback.h: Added.
  • dfg/DFGDriver.cpp:

(JSC::DFG::tryCompile):

  • dfg/DFGDriver.h:

(JSC::DFG::tryCompile):

  • dfg/DFGFailedFinalizer.cpp:

(JSC::DFG::FailedFinalizer::finalize):
(JSC::DFG::FailedFinalizer::finalizeFunction):

  • dfg/DFGFailedFinalizer.h:
  • dfg/DFGFinalizer.h:
  • dfg/DFGJITFinalizer.cpp:

(JSC::DFG::JITFinalizer::finalize):
(JSC::DFG::JITFinalizer::finalizeFunction):

  • dfg/DFGJITFinalizer.h:
  • dfg/DFGOSRExitPreparation.cpp:

(JSC::DFG::prepareCodeOriginForOSRExit):

  • dfg/DFGOperations.cpp:
  • dfg/DFGPlan.cpp:

(JSC::DFG::Plan::Plan):
(JSC::DFG::Plan::compileInThreadImpl):
(JSC::DFG::Plan::finalizeWithoutNotifyingCallback):
(JSC::DFG::Plan::finalizeAndNotifyCallback):

  • dfg/DFGPlan.h:
  • dfg/DFGWorklist.cpp:

(JSC::DFG::Worklist::completeAllReadyPlansForVM):

  • ftl/FTLJITFinalizer.cpp:

(JSC::FTL::JITFinalizer::finalize):
(JSC::FTL::JITFinalizer::finalizeFunction):

  • ftl/FTLJITFinalizer.h:
  • heap/Heap.h:

(JSC::Heap::isDeferred):

  • interpreter/Interpreter.cpp:

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

  • jit/JITDriver.h: Removed.
  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):
(JSC::jitCompileFor):
(JSC::lazyLinkFor):

  • jit/JITToDFGDeferredCompilationCallback.cpp: Added.

(JSC::JITToDFGDeferredCompilationCallback::JITToDFGDeferredCompilationCallback):
(JSC::JITToDFGDeferredCompilationCallback::~JITToDFGDeferredCompilationCallback):
(JSC::JITToDFGDeferredCompilationCallback::create):
(JSC::JITToDFGDeferredCompilationCallback::compilationDidComplete):

  • jit/JITToDFGDeferredCompilationCallback.h: Added.
  • llint/LLIntEntrypoints.cpp:

(JSC::LLInt::setFunctionEntrypoint):
(JSC::LLInt::setEvalEntrypoint):
(JSC::LLInt::setProgramEntrypoint):

  • llint/LLIntEntrypoints.h:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::jitCompileAndSetHeuristics):
(JSC::LLInt::setUpCall):

  • runtime/ArrayPrototype.cpp:

(JSC::isNumericCompareFunction):

  • runtime/CommonSlowPaths.cpp:
  • runtime/CompilationResult.cpp:

(WTF::printInternal):

  • runtime/CompilationResult.h:
  • runtime/Executable.cpp:

(JSC::ScriptExecutable::installCode):
(JSC::ScriptExecutable::newCodeBlockFor):
(JSC::ScriptExecutable::newReplacementCodeBlockFor):
(JSC::ScriptExecutable::prepareForExecutionImpl):

  • runtime/Executable.h:

(JSC::ScriptExecutable::prepareForExecution):
(JSC::FunctionExecutable::jettisonOptimizedCodeFor):

  • runtime/ExecutionHarness.h: Removed.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r154162 r154804  
    3535#include "DFGCapabilities.h"
    3636#include "DFGCommon.h"
     37#include "DFGDriver.h"
    3738#include "DFGNode.h"
    3839#include "DFGRepatch.h"
     
    4647#include "JSFunction.h"
    4748#include "JSNameScope.h"
     49#include "LLIntEntrypoints.h"
    4850#include "LowLevelInterpreter.h"
    4951#include "Operations.h"
     
    25372539    m_incomingCalls.push(incoming);
    25382540}
     2541#endif // ENABLE(JIT)
    25392542
    25402543void CodeBlock::unlinkIncomingCalls()
     
    25432546    while (m_incomingLLIntCalls.begin() != m_incomingLLIntCalls.end())
    25442547        m_incomingLLIntCalls.begin()->unlink();
    2545 #endif
     2548#endif // ENABLE(LLINT)
     2549#if ENABLE(JIT)
    25462550    if (m_incomingCalls.isEmpty())
    25472551        return;
     
    25492553    while (m_incomingCalls.begin() != m_incomingCalls.end())
    25502554        m_incomingCalls.begin()->unlink(*m_vm, repatchBuffer);
    2551 }
    25522555#endif // ENABLE(JIT)
     2556}
    25532557
    25542558#if ENABLE(LLINT)
     
    26902694}
    26912695
     2696CompilationResult CodeBlock::prepareForExecutionImpl(
     2697    ExecState* exec, JITCode::JITType jitType, JITCompilationEffort effort,
     2698    unsigned bytecodeIndex, PassRefPtr<DeferredCompilationCallback> callback)
     2699{
     2700    VM& vm = exec->vm();
     2701   
     2702    if (jitType == JITCode::InterpreterThunk) {
     2703        switch (codeType()) {
     2704        case GlobalCode:
     2705            LLInt::setProgramEntrypoint(vm, static_cast<ProgramCodeBlock*>(this));
     2706            break;
     2707        case EvalCode:
     2708            LLInt::setEvalEntrypoint(vm, static_cast<EvalCodeBlock*>(this));
     2709            break;
     2710        case FunctionCode:
     2711            LLInt::setFunctionEntrypoint(vm, static_cast<FunctionCodeBlock*>(this));
     2712            break;
     2713        }
     2714        return CompilationSuccessful;
     2715    }
     2716   
     2717#if ENABLE(JIT)
     2718    if (JITCode::isOptimizingJIT(jitType)) {
     2719        ASSERT(effort == JITCompilationCanFail);
     2720        bool hadCallback = !!callback;
     2721        CompilationResult result = DFG::tryCompile(exec, this, bytecodeIndex, callback);
     2722        ASSERT_UNUSED(hadCallback, result != CompilationDeferred || hadCallback);
     2723        return result;
     2724    }
     2725   
     2726    MacroAssemblerCodePtr jitCodeWithArityCheck;
     2727    RefPtr<JITCode> jitCode = JIT::compile(&vm, this, effort, &jitCodeWithArityCheck);
     2728    if (!jitCode)
     2729        return CompilationFailed;
     2730    setJITCode(jitCode, jitCodeWithArityCheck);
     2731    return CompilationSuccessful;
     2732#else
     2733    UNUSED_PARAM(effort);
     2734    UNUSED_PARAM(bytecodeIndex);
     2735    UNUSED_PARAM(callback);
     2736    return CompilationFailed;
     2737#endif // ENABLE(JIT)
     2738}
     2739
     2740CompilationResult CodeBlock::prepareForExecution(
     2741    ExecState* exec, JITCode::JITType jitType,
     2742    JITCompilationEffort effort, unsigned bytecodeIndex)
     2743{
     2744    CompilationResult result =
     2745        prepareForExecutionImpl(exec, jitType, effort, bytecodeIndex, 0);
     2746    ASSERT(result != CompilationDeferred);
     2747    return result;
     2748}
     2749
     2750CompilationResult CodeBlock::prepareForExecutionAsynchronously(
     2751    ExecState* exec, JITCode::JITType jitType,
     2752    PassRefPtr<DeferredCompilationCallback> passedCallback,
     2753    JITCompilationEffort effort, unsigned bytecodeIndex)
     2754{
     2755    RefPtr<DeferredCompilationCallback> callback = passedCallback;
     2756    CompilationResult result =
     2757        prepareForExecutionImpl(exec, jitType, effort, bytecodeIndex, callback);
     2758    if (result != CompilationDeferred)
     2759        callback->compilationDidComplete(this, result);
     2760    return result;
     2761}
     2762
     2763void CodeBlock::install()
     2764{
     2765    ownerExecutable()->installCode(this);
     2766}
     2767
     2768PassRefPtr<CodeBlock> CodeBlock::newReplacement()
     2769{
     2770    return ownerExecutable()->newReplacementCodeBlockFor(specializationKind());
     2771}
     2772
    26922773#if ENABLE(JIT)
    26932774void CodeBlock::reoptimize()
     
    27152796    return &static_cast<FunctionExecutable*>(ownerExecutable())->generatedBytecodeFor(m_isConstructor ? CodeForConstruct : CodeForCall);
    27162797}
    2717 
    2718 #if ENABLE(DFG_JIT)
    2719 JSObject* ProgramCodeBlock::compileOptimized(ExecState* exec, JSScope* scope, CompilationResult& result, unsigned bytecodeIndex)
    2720 {
    2721     if (JITCode::isHigherTier(replacement()->jitType(), jitType())) {
    2722         result = CompilationNotNeeded;
    2723         return 0;
    2724     }
    2725     JSObject* error = static_cast<ProgramExecutable*>(ownerExecutable())->compileOptimized(exec, scope, result, bytecodeIndex);
    2726     return error;
    2727 }
    2728 
    2729 CompilationResult ProgramCodeBlock::replaceWithDeferredOptimizedCode(PassRefPtr<DFG::Plan> plan)
    2730 {
    2731     return static_cast<ProgramExecutable*>(ownerExecutable())->replaceWithDeferredOptimizedCode(plan);
    2732 }
    2733 
    2734 JSObject* EvalCodeBlock::compileOptimized(ExecState* exec, JSScope* scope, CompilationResult& result, unsigned bytecodeIndex)
    2735 {
    2736     if (JITCode::isHigherTier(replacement()->jitType(), jitType())) {
    2737         result = CompilationNotNeeded;
    2738         return 0;
    2739     }
    2740     JSObject* error = static_cast<EvalExecutable*>(ownerExecutable())->compileOptimized(exec, scope, result, bytecodeIndex);
    2741     return error;
    2742 }
    2743 
    2744 CompilationResult EvalCodeBlock::replaceWithDeferredOptimizedCode(PassRefPtr<DFG::Plan> plan)
    2745 {
    2746     return static_cast<EvalExecutable*>(ownerExecutable())->replaceWithDeferredOptimizedCode(plan);
    2747 }
    2748 
    2749 JSObject* FunctionCodeBlock::compileOptimized(ExecState* exec, JSScope* scope, CompilationResult& result, unsigned bytecodeIndex)
    2750 {
    2751     if (JITCode::isHigherTier(replacement()->jitType(), jitType())) {
    2752         result = CompilationNotNeeded;
    2753         return 0;
    2754     }
    2755     JSObject* error = static_cast<FunctionExecutable*>(ownerExecutable())->compileOptimizedFor(exec, scope, result, bytecodeIndex, m_isConstructor ? CodeForConstruct : CodeForCall);
    2756     return error;
    2757 }
    2758 
    2759 CompilationResult FunctionCodeBlock::replaceWithDeferredOptimizedCode(PassRefPtr<DFG::Plan> plan)
    2760 {
    2761     return static_cast<FunctionExecutable*>(ownerExecutable())->replaceWithDeferredOptimizedCodeFor(plan, m_isConstructor ? CodeForConstruct : CodeForCall);
    2762 }
    2763 #endif // ENABLE(DFG_JIT)
    27642798
    27652799DFG::CapabilityLevel ProgramCodeBlock::capabilityLevelInternal()
     
    28042838{
    28052839    static_cast<FunctionExecutable*>(ownerExecutable())->jettisonOptimizedCodeFor(*vm(), m_isConstructor ? CodeForConstruct : CodeForCall);
    2806 }
    2807 
    2808 CompilationResult ProgramCodeBlock::jitCompileImpl(ExecState* exec)
    2809 {
    2810     ASSERT(jitType() == JITCode::InterpreterThunk);
    2811     ASSERT(this == replacement());
    2812     return static_cast<ProgramExecutable*>(ownerExecutable())->jitCompile(exec);
    2813 }
    2814 
    2815 CompilationResult EvalCodeBlock::jitCompileImpl(ExecState* exec)
    2816 {
    2817     ASSERT(jitType() == JITCode::InterpreterThunk);
    2818     ASSERT(this == replacement());
    2819     return static_cast<EvalExecutable*>(ownerExecutable())->jitCompile(exec);
    2820 }
    2821 
    2822 CompilationResult FunctionCodeBlock::jitCompileImpl(ExecState* exec)
    2823 {
    2824     ASSERT(jitType() == JITCode::InterpreterThunk);
    2825     ASSERT(this == replacement());
    2826     return static_cast<FunctionExecutable*>(ownerExecutable())->jitCompileFor(exec, m_isConstructor ? CodeForConstruct : CodeForCall);
    28272840}
    28282841#endif
Note: See TracChangeset for help on using the changeset viewer.