]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGPlan.cpp
JavaScriptCore-7600.1.4.9.tar.gz
[apple/javascriptcore.git] / dfg / DFGPlan.cpp
diff --git a/dfg/DFGPlan.cpp b/dfg/DFGPlan.cpp
new file mode 100644 (file)
index 0000000..c78a307
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGPlan.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGArgumentsSimplificationPhase.h"
+#include "DFGBackwardsPropagationPhase.h"
+#include "DFGByteCodeParser.h"
+#include "DFGCFAPhase.h"
+#include "DFGCFGSimplificationPhase.h"
+#include "DFGCPSRethreadingPhase.h"
+#include "DFGCSEPhase.h"
+#include "DFGConstantFoldingPhase.h"
+#include "DFGCriticalEdgeBreakingPhase.h"
+#include "DFGDCEPhase.h"
+#include "DFGFailedFinalizer.h"
+#include "DFGFixupPhase.h"
+#include "DFGGraphSafepoint.h"
+#include "DFGIntegerCheckCombiningPhase.h"
+#include "DFGInvalidationPointInjectionPhase.h"
+#include "DFGJITCompiler.h"
+#include "DFGLICMPhase.h"
+#include "DFGLivenessAnalysisPhase.h"
+#include "DFGLoopPreHeaderCreationPhase.h"
+#include "DFGOSRAvailabilityAnalysisPhase.h"
+#include "DFGOSREntrypointCreationPhase.h"
+#include "DFGPredictionInjectionPhase.h"
+#include "DFGPredictionPropagationPhase.h"
+#include "DFGResurrectionForValidationPhase.h"
+#include "DFGSSAConversionPhase.h"
+#include "DFGSSALoweringPhase.h"
+#include "DFGStackLayoutPhase.h"
+#include "DFGStaticExecutionCountEstimationPhase.h"
+#include "DFGStoreBarrierElisionPhase.h"
+#include "DFGStrengthReductionPhase.h"
+#include "DFGTierUpCheckInjectionPhase.h"
+#include "DFGTypeCheckHoistingPhase.h"
+#include "DFGUnificationPhase.h"
+#include "DFGValidate.h"
+#include "DFGVirtualRegisterAllocationPhase.h"
+#include "DFGWatchpointCollectionPhase.h"
+#include "Debugger.h"
+#include "JSCInlines.h"
+#include "OperandsInlines.h"
+#include "ProfilerDatabase.h"
+#include <wtf/CurrentTime.h>
+
+#if ENABLE(FTL_JIT)
+#include "FTLCapabilities.h"
+#include "FTLCompile.h"
+#include "FTLFail.h"
+#include "FTLLink.h"
+#include "FTLLowerDFGToLLVM.h"
+#include "FTLState.h"
+#include "InitializeLLVM.h"
+#endif
+
+namespace JSC { namespace DFG {
+
+static void dumpAndVerifyGraph(Graph& graph, const char* text)
+{
+    GraphDumpMode modeForFinalValidate = DumpGraph;
+    if (verboseCompilationEnabled(graph.m_plan.mode)) {
+        dataLog(text, "\n");
+        graph.dump();
+        modeForFinalValidate = DontDumpGraph;
+    }
+    if (validationEnabled())
+        validate(graph, modeForFinalValidate);
+}
+
+static Profiler::CompilationKind profilerCompilationKindForMode(CompilationMode mode)
+{
+    switch (mode) {
+    case InvalidCompilationMode:
+        RELEASE_ASSERT_NOT_REACHED();
+        return Profiler::DFG;
+    case DFGMode:
+        return Profiler::DFG;
+    case FTLMode:
+        return Profiler::FTL;
+    case FTLForOSREntryMode:
+        return Profiler::FTLForOSREntry;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+    return Profiler::DFG;
+}
+
+Plan::Plan(PassRefPtr<CodeBlock> passedCodeBlock, CodeBlock* profiledDFGCodeBlock,
+    CompilationMode mode, unsigned osrEntryBytecodeIndex,
+    const Operands<JSValue>& mustHandleValues)
+    : vm(*passedCodeBlock->vm())
+    , codeBlock(passedCodeBlock)
+    , profiledDFGCodeBlock(profiledDFGCodeBlock)
+    , mode(mode)
+    , osrEntryBytecodeIndex(osrEntryBytecodeIndex)
+    , mustHandleValues(mustHandleValues)
+    , compilation(codeBlock->vm()->m_perBytecodeProfiler ? adoptRef(new Profiler::Compilation(codeBlock->vm()->m_perBytecodeProfiler->ensureBytecodesFor(codeBlock.get()), profilerCompilationKindForMode(mode))) : 0)
+    , inlineCallFrames(adoptRef(new InlineCallFrameSet()))
+    , identifiers(codeBlock.get())
+    , weakReferences(codeBlock.get())
+    , willTryToTierUp(false)
+    , stage(Preparing)
+{
+}
+
+Plan::~Plan()
+{
+}
+
+bool Plan::reportCompileTimes() const
+{
+    return Options::reportCompileTimes()
+        || (Options::reportFTLCompileTimes() && isFTL(mode));
+}
+
+void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadData)
+{
+    this->threadData = threadData;
+    
+    double before = 0;
+    CString codeBlockName;
+    if (reportCompileTimes()) {
+        before = currentTimeMS();
+        codeBlockName = toCString(*codeBlock);
+    }
+    
+    SamplingRegion samplingRegion("DFG Compilation (Plan)");
+    CompilationScope compilationScope;
+
+    if (logCompilationChanges(mode))
+        dataLog("DFG(Plan) compiling ", *codeBlock, " with ", mode, ", number of instructions = ", codeBlock->instructionCount(), "\n");
+
+    CompilationPath path = compileInThreadImpl(longLivedState);
+
+    RELEASE_ASSERT(path == CancelPath || finalizer);
+    RELEASE_ASSERT((path == CancelPath) == (stage == Cancelled));
+    
+    if (reportCompileTimes()) {
+        const char* pathName;
+        switch (path) {
+        case FailPath:
+            pathName = "N/A (fail)";
+            break;
+        case DFGPath:
+            pathName = "DFG";
+            break;
+        case FTLPath:
+            pathName = "FTL";
+            break;
+        case CancelPath:
+            pathName = "Cancelled";
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            pathName = "";
+            break;
+        }
+        double now = currentTimeMS();
+        dataLog("Optimized ", codeBlockName, " using ", mode, " with ", pathName, " into ", finalizer ? finalizer->codeSize() : 0, " bytes in ", now - before, " ms");
+        if (path == FTLPath)
+            dataLog(" (DFG: ", beforeFTL - before, ", LLVM: ", now - beforeFTL, ")");
+        dataLog(".\n");
+    }
+}
+
+Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
+{
+    if (verboseCompilationEnabled(mode) && osrEntryBytecodeIndex != UINT_MAX) {
+        dataLog("\n");
+        dataLog("Compiler must handle OSR entry from bc#", osrEntryBytecodeIndex, " with values: ", mustHandleValues, "\n");
+        dataLog("\n");
+    }
+    
+    Graph dfg(vm, *this, longLivedState);
+    
+    if (!parse(dfg)) {
+        finalizer = adoptPtr(new FailedFinalizer(*this));
+        return FailPath;
+    }
+    
+    // By this point the DFG bytecode parser will have potentially mutated various tables
+    // in the CodeBlock. This is a good time to perform an early shrink, which is more
+    // powerful than a late one. It's safe to do so because we haven't generated any code
+    // that references any of the tables directly, yet.
+    codeBlock->shrinkToFit(CodeBlock::EarlyShrink);
+
+    if (validationEnabled())
+        validate(dfg);
+    
+    performCPSRethreading(dfg);
+    performUnification(dfg);
+    performPredictionInjection(dfg);
+    
+    performStaticExecutionCountEstimation(dfg);
+    
+    if (mode == FTLForOSREntryMode) {
+        bool result = performOSREntrypointCreation(dfg);
+        if (!result) {
+            finalizer = adoptPtr(new FailedFinalizer(*this));
+            return FailPath;
+        }
+        performCPSRethreading(dfg);
+    }
+    
+    if (validationEnabled())
+        validate(dfg);
+    
+    performBackwardsPropagation(dfg);
+    performPredictionPropagation(dfg);
+    performFixup(dfg);
+    performInvalidationPointInjection(dfg);
+    performTypeCheckHoisting(dfg);
+    
+    unsigned count = 1;
+    dfg.m_fixpointState = FixpointNotConverged;
+    for (;; ++count) {
+        if (logCompilationChanges(mode))
+            dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", count);
+        bool changed = false;
+        
+        if (validationEnabled())
+            validate(dfg);
+        
+        changed |= performStrengthReduction(dfg);
+        performCFA(dfg);
+        changed |= performConstantFolding(dfg);
+        changed |= performArgumentsSimplification(dfg);
+        changed |= performCFGSimplification(dfg);
+        changed |= performCSE(dfg);
+        
+        if (!changed)
+            break;
+        
+        performCPSRethreading(dfg);
+    }
+    
+    if (logCompilationChanges(mode))
+        dataLogF("DFG optimization fixpoint converged in %u iterations.\n", count);
+
+    dfg.m_fixpointState = FixpointConverged;
+
+    // If we're doing validation, then run some analyses, to give them an opportunity
+    // to self-validate. Now is as good a time as any to do this.
+    if (validationEnabled()) {
+        dfg.m_dominators.computeIfNecessary(dfg);
+        dfg.m_naturalLoops.computeIfNecessary(dfg);
+    }
+
+    switch (mode) {
+    case DFGMode: {
+        performTierUpCheckInjection(dfg);
+
+        performStoreBarrierElision(dfg);
+        performStoreElimination(dfg);
+        performCPSRethreading(dfg);
+        performDCE(dfg);
+        performStackLayout(dfg);
+        performVirtualRegisterAllocation(dfg);
+        performWatchpointCollection(dfg);
+        dumpAndVerifyGraph(dfg, "Graph after optimization:");
+        
+        JITCompiler dataFlowJIT(dfg);
+        if (codeBlock->codeType() == FunctionCode) {
+            dataFlowJIT.compileFunction();
+            dataFlowJIT.linkFunction();
+        } else {
+            dataFlowJIT.compile();
+            dataFlowJIT.link();
+        }
+        
+        return DFGPath;
+    }
+    
+    case FTLMode:
+    case FTLForOSREntryMode: {
+#if ENABLE(FTL_JIT)
+        if (FTL::canCompile(dfg) == FTL::CannotCompile) {
+            finalizer = adoptPtr(new FailedFinalizer(*this));
+            return FailPath;
+        }
+        
+        performCriticalEdgeBreaking(dfg);
+        performLoopPreHeaderCreation(dfg);
+        performCPSRethreading(dfg);
+        performSSAConversion(dfg);
+        performSSALowering(dfg);
+        performCSE(dfg);
+        performLivenessAnalysis(dfg);
+        performCFA(dfg);
+        performLICM(dfg);
+        performIntegerCheckCombining(dfg);
+        performCSE(dfg);
+        
+        // At this point we're not allowed to do any further code motion because our reasoning
+        // about code motion assumes that it's OK to insert GC points in random places.
+        
+        performStoreBarrierElision(dfg);
+        performLivenessAnalysis(dfg);
+        performCFA(dfg);
+        if (Options::validateFTLOSRExitLiveness())
+            performResurrectionForValidation(dfg);
+        performDCE(dfg); // We rely on this to convert dead SetLocals into the appropriate hint, and to kill dead code that won't be recognized as dead by LLVM.
+        performStackLayout(dfg);
+        performLivenessAnalysis(dfg);
+        performOSRAvailabilityAnalysis(dfg);
+        performWatchpointCollection(dfg);
+        
+        dumpAndVerifyGraph(dfg, "Graph just before FTL lowering:");
+        
+        bool haveLLVM;
+        Safepoint::Result safepointResult;
+        {
+            GraphSafepoint safepoint(dfg, safepointResult);
+            haveLLVM = initializeLLVM();
+        }
+        if (safepointResult.didGetCancelled())
+            return CancelPath;
+        
+        if (!haveLLVM) {
+            finalizer = adoptPtr(new FailedFinalizer(*this));
+            return FailPath;
+        }
+            
+        FTL::State state(dfg);
+        FTL::lowerDFGToLLVM(state);
+        
+        if (reportCompileTimes())
+            beforeFTL = currentTimeMS();
+        
+        if (Options::llvmAlwaysFailsBeforeCompile()) {
+            FTL::fail(state);
+            return FTLPath;
+        }
+        
+        FTL::compile(state, safepointResult);
+        if (safepointResult.didGetCancelled())
+            return CancelPath;
+        
+        if (Options::llvmAlwaysFailsBeforeLink()) {
+            FTL::fail(state);
+            return FTLPath;
+        }
+
+        if (state.jitCode->stackmaps.stackSize() > Options::llvmMaxStackSize()) {
+            FTL::fail(state);
+            return FTLPath;
+        }
+
+        FTL::link(state);
+        return FTLPath;
+#else
+        RELEASE_ASSERT_NOT_REACHED();
+        return FailPath;
+#endif // ENABLE(FTL_JIT)
+    }
+        
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        return FailPath;
+    }
+}
+
+bool Plan::isStillValid()
+{
+    CodeBlock* replacement = codeBlock->replacement();
+    if (!replacement)
+        return false;
+    // FIXME: This is almost certainly not necessary. There's no way for the baseline
+    // code to be replaced during a compilation, except if we delete the plan, in which
+    // case we wouldn't be here.
+    // https://bugs.webkit.org/show_bug.cgi?id=132707
+    if (codeBlock->alternative() != replacement->baselineVersion())
+        return false;
+    if (!watchpoints.areStillValid())
+        return false;
+    if (!chains.areStillValid())
+        return false;
+    return true;
+}
+
+void Plan::reallyAdd(CommonData* commonData)
+{
+    watchpoints.reallyAdd(codeBlock.get(), *commonData);
+    identifiers.reallyAdd(vm, commonData);
+    weakReferences.reallyAdd(vm, commonData);
+    transitions.reallyAdd(vm, commonData);
+    writeBarriers.trigger(vm);
+}
+
+void Plan::notifyCompiling()
+{
+    stage = Compiling;
+}
+
+void Plan::notifyCompiled()
+{
+    stage = Compiled;
+}
+
+void Plan::notifyReady()
+{
+    callback->compilationDidBecomeReadyAsynchronously(codeBlock.get());
+    stage = Ready;
+}
+
+CompilationResult Plan::finalizeWithoutNotifyingCallback()
+{
+    if (!isStillValid())
+        return CompilationInvalidated;
+
+    bool result;
+    if (codeBlock->codeType() == FunctionCode)
+        result = finalizer->finalizeFunction();
+    else
+        result = finalizer->finalize();
+    
+    if (!result)
+        return CompilationFailed;
+    
+    reallyAdd(codeBlock->jitCode()->dfgCommon());
+    
+    return CompilationSuccessful;
+}
+
+void Plan::finalizeAndNotifyCallback()
+{
+    callback->compilationDidComplete(codeBlock.get(), finalizeWithoutNotifyingCallback());
+}
+
+CompilationKey Plan::key()
+{
+    return CompilationKey(codeBlock->alternative(), mode);
+}
+
+void Plan::checkLivenessAndVisitChildren(SlotVisitor& visitor, CodeBlockSet& codeBlocks)
+{
+    if (!isKnownToBeLiveDuringGC())
+        return;
+    
+    for (unsigned i = mustHandleValues.size(); i--;)
+        visitor.appendUnbarrieredValue(&mustHandleValues[i]);
+    
+    codeBlocks.mark(codeBlock->alternative());
+    codeBlocks.mark(codeBlock.get());
+    codeBlocks.mark(profiledDFGCodeBlock.get());
+    
+    chains.visitChildren(visitor);
+    weakReferences.visitChildren(visitor);
+    writeBarriers.visitChildren(visitor);
+    transitions.visitChildren(visitor);
+}
+
+bool Plan::isKnownToBeLiveDuringGC()
+{
+    if (stage == Cancelled)
+        return false;
+    if (!Heap::isMarked(codeBlock->ownerExecutable()))
+        return false;
+    if (!codeBlock->alternative()->isKnownToBeLiveDuringGC())
+        return false;
+    if (!!profiledDFGCodeBlock && !profiledDFGCodeBlock->isKnownToBeLiveDuringGC())
+        return false;
+    return true;
+}
+
+void Plan::cancel()
+{
+    codeBlock = nullptr;
+    profiledDFGCodeBlock = nullptr;
+    mustHandleValues.clear();
+    compilation = nullptr;
+    finalizer.clear();
+    inlineCallFrames = nullptr;
+    watchpoints = DesiredWatchpoints();
+    identifiers = DesiredIdentifiers();
+    chains = DesiredStructureChains();
+    weakReferences = DesiredWeakReferences();
+    writeBarriers = DesiredWriteBarriers();
+    transitions = DesiredTransitions();
+    callback = nullptr;
+    stage = Cancelled;
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+