static void dumpAndVerifyGraph(Graph& graph, const char* text) { GraphDumpMode modeForFinalValidate = DumpGraph; if (verboseCompilationEnabled()) { dataLog(text, "\n"); graph.dump(); modeForFinalValidate = DontDumpGraph; } if (validationEnabled()) validate(graph, modeForFinalValidate); }
CompilationResult Plan::finalizeWithoutNotifyingCallback() { // We will establish new references from the code block to things. So, we need a barrier. vm->heap.writeBarrier(codeBlock); if (!isStillValid()) { CODEBLOCK_LOG_EVENT(codeBlock, "dfgFinalize", ("invalidated")); return CompilationInvalidated; } bool result; if (codeBlock->codeType() == FunctionCode) result = finalizer->finalizeFunction(); else result = finalizer->finalize(); if (!result) { CODEBLOCK_LOG_EVENT(codeBlock, "dfgFinalize", ("failed")); return CompilationFailed; } reallyAdd(codeBlock->jitCode()->dfgCommon()); if (validationEnabled()) { TrackedReferences trackedReferences; for (WriteBarrier<JSCell>& reference : codeBlock->jitCode()->dfgCommon()->weakReferences) trackedReferences.add(reference.get()); for (WriteBarrier<Structure>& reference : codeBlock->jitCode()->dfgCommon()->weakStructureReferences) trackedReferences.add(reference.get()); for (WriteBarrier<Unknown>& constant : codeBlock->constants()) trackedReferences.add(constant.get()); // Check that any other references that we have anywhere in the JITCode are also // tracked either strongly or weakly. codeBlock->jitCode()->validateReferences(trackedReferences); } CODEBLOCK_LOG_EVENT(codeBlock, "dfgFinalize", ("succeeded")); return CompilationSuccessful; }
Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState) { if (verboseCompilationEnabled() && 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); 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); performTypeCheckHoisting(dfg); unsigned count = 1; dfg.m_fixpointState = FixpointNotConverged; for (;; ++count) { if (logCompilationChanges()) dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", count); bool changed = false; if (validationEnabled()) validate(dfg); performCFA(dfg); changed |= performConstantFolding(dfg); changed |= performArgumentsSimplification(dfg); changed |= performCFGSimplification(dfg); changed |= performCSE(dfg); if (!changed) break; performCPSRethreading(dfg); } if (logCompilationChanges()) dataLogF("DFG optimization fixpoint converged in %u iterations.\n", count); dfg.m_fixpointState = FixpointConverged; performStoreElimination(dfg); // 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); break; } 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); performLivenessAnalysis(dfg); performCFA(dfg); performLICM(dfg); performLivenessAnalysis(dfg); performCFA(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); performFlushLivenessAnalysis(dfg); performOSRAvailabilityAnalysis(dfg); dumpAndVerifyGraph(dfg, "Graph just before FTL lowering:"); initializeLLVM(); FTL::State state(dfg); FTL::lowerDFGToLLVM(state); if (Options::reportCompileTimes()) beforeFTL = currentTimeMS(); if (Options::llvmAlwaysFailsBeforeCompile()) { FTL::fail(state); return FTLPath; } FTL::compile(state); if (Options::llvmAlwaysFailsBeforeLink()) { FTL::fail(state); return FTLPath; } FTL::link(state); return FTLPath; #else RELEASE_ASSERT_NOT_REACHED(); break; #endif // ENABLE(FTL_JIT) } default: RELEASE_ASSERT_NOT_REACHED(); break; } performCPSRethreading(dfg); performDCE(dfg); performStackLayout(dfg); performVirtualRegisterAllocation(dfg); dumpAndVerifyGraph(dfg, "Graph after optimization:"); JITCompiler dataFlowJIT(dfg); if (codeBlock->codeType() == FunctionCode) { dataFlowJIT.compileFunction(); dataFlowJIT.linkFunction(); } else { dataFlowJIT.compile(); dataFlowJIT.link(); } return DFGPath; }
bool run() { ASSERT(m_graph.m_form == SSA); for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; block->ssa->availabilityAtHead.clear(); block->ssa->availabilityAtTail.clear(); } BasicBlock* root = m_graph.block(0); root->ssa->availabilityAtHead.m_locals.fill(Availability::unavailable()); for (unsigned argument = m_graph.m_argumentFormats.size(); argument--;) { FlushedAt flushedAt = FlushedAt( m_graph.m_argumentFormats[argument], virtualRegisterForArgument(argument)); root->ssa->availabilityAtHead.m_locals.argument(argument) = Availability(flushedAt); } // This could be made more efficient by processing blocks in reverse postorder. LocalOSRAvailabilityCalculator calculator(m_graph); bool changed; do { changed = false; for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; calculator.beginBlock(block); for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) calculator.executeNode(block->at(nodeIndex)); if (calculator.m_availability == block->ssa->availabilityAtTail) continue; block->ssa->availabilityAtTail = calculator.m_availability; changed = true; for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) { BasicBlock* successor = block->successor(successorIndex); successor->ssa->availabilityAtHead.merge(calculator.m_availability); successor->ssa->availabilityAtHead.pruneByLiveness( m_graph, successor->at(0)->origin.forExit); } } } while (changed); if (validationEnabled()) { for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; calculator.beginBlock(block); for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { if (block->at(nodeIndex)->origin.exitOK) { // If we're allowed to exit here, the heap must be in a state // where exiting wouldn't crash. These particular fields are // required for correctness because we use them during OSR exit // to do meaningful things. It would be wrong for any of them // to be dead. AvailabilityMap availabilityMap = calculator.m_availability; availabilityMap.pruneByLiveness(m_graph, block->at(nodeIndex)->origin.forExit); for (auto heapPair : availabilityMap.m_heap) { switch (heapPair.key.kind()) { case ActivationScopePLoc: case ActivationSymbolTablePLoc: case FunctionActivationPLoc: case FunctionExecutablePLoc: case StructurePLoc: if (heapPair.value.isDead()) { dataLogLn("PromotedHeapLocation is dead, but should not be: ", heapPair.key); availabilityMap.dump(WTF::dataFile()); CRASH(); } break; default: break; } } } calculator.executeNode(block->at(nodeIndex)); } } } return true; }
Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState) { cleanMustHandleValuesIfNecessary(); 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 = std::make_unique<FailedFinalizer>(*this); return FailPath; } codeBlock->setCalleeSaveRegisters(RegisterSet::dfgCalleeSaveRegisters()); // 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); if (Options::dumpGraphAfterParsing()) { dataLog("Graph after parsing:\n"); dfg.dump(); } performLiveCatchVariablePreservationPhase(dfg); if (Options::useMaximalFlushInsertionPhase()) performMaximalFlushInsertion(dfg); performCPSRethreading(dfg); performUnification(dfg); performPredictionInjection(dfg); performStaticExecutionCountEstimation(dfg); if (mode == FTLForOSREntryMode) { bool result = performOSREntrypointCreation(dfg); if (!result) { finalizer = std::make_unique<FailedFinalizer>(*this); return FailPath; } performCPSRethreading(dfg); } if (validationEnabled()) validate(dfg); performBackwardsPropagation(dfg); performPredictionPropagation(dfg); performFixup(dfg); performStructureRegistration(dfg); performInvalidationPointInjection(dfg); performTypeCheckHoisting(dfg); dfg.m_fixpointState = FixpointNotConverged; // For now we're back to avoiding a fixpoint. Note that we've ping-ponged on this decision // many times. For maximum throughput, it's best to fixpoint. But the throughput benefit is // small and not likely to show up in FTL anyway. On the other hand, not fixpointing means // that the compiler compiles more quickly. We want the third tier to compile quickly, which // not fixpointing accomplishes; and the fourth tier shouldn't need a fixpoint. if (validationEnabled()) validate(dfg); performStrengthReduction(dfg); performCPSRethreading(dfg); performCFA(dfg); performConstantFolding(dfg); bool changed = false; changed |= performCFGSimplification(dfg); changed |= performLocalCSE(dfg); if (validationEnabled()) validate(dfg); performCPSRethreading(dfg); if (!isFTL(mode)) { // Only run this if we're not FTLing, because currently for a LoadVarargs that is forwardable and // in a non-varargs inlined call frame, this will generate ForwardVarargs while the FTL // ArgumentsEliminationPhase will create a sequence of GetStack+PutStacks. The GetStack+PutStack // sequence then gets sunk, eliminating anything that looks like an escape for subsequent phases, // while the ForwardVarargs doesn't get simplified until later (or not at all) and looks like an // escape for all of the arguments. This then disables object allocation sinking. // // So, for now, we just disable this phase for the FTL. // // If we wanted to enable it, we'd have to do any of the following: // - Enable ForwardVarargs->GetStack+PutStack strength reduction, and have that run before // PutStack sinking and object allocation sinking. // - Make VarargsForwarding emit a GetLocal+SetLocal sequence, that we can later turn into // GetStack+PutStack. // // But, it's not super valuable to enable those optimizations, since the FTL // ArgumentsEliminationPhase does everything that this phase does, and it doesn't introduce this // pathology. changed |= performVarargsForwarding(dfg); // Do this after CFG simplification and CPS rethreading. } if (changed) { performCFA(dfg); performConstantFolding(dfg); } // 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.ensureDominators(); dfg.ensureNaturalLoops(); dfg.ensurePrePostNumbering(); } switch (mode) { case DFGMode: { dfg.m_fixpointState = FixpointConverged; performTierUpCheckInjection(dfg); performFastStoreBarrierInsertion(dfg); performStoreBarrierClustering(dfg); performCleanUp(dfg); performCPSRethreading(dfg); performDCE(dfg); performPhantomInsertion(dfg); performStackLayout(dfg); performVirtualRegisterAllocation(dfg); performWatchpointCollection(dfg); dumpAndVerifyGraph(dfg, "Graph after optimization:"); JITCompiler dataFlowJIT(dfg); if (codeBlock->codeType() == FunctionCode) dataFlowJIT.compileFunction(); else dataFlowJIT.compile(); return DFGPath; } case FTLMode: case FTLForOSREntryMode: { #if ENABLE(FTL_JIT) if (FTL::canCompile(dfg) == FTL::CannotCompile) { finalizer = std::make_unique<FailedFinalizer>(*this); return FailPath; } performCleanUp(dfg); // Reduce the graph size a bit. performCriticalEdgeBreaking(dfg); if (Options::createPreHeaders()) performLoopPreHeaderCreation(dfg); performCPSRethreading(dfg); performSSAConversion(dfg); performSSALowering(dfg); // Ideally, these would be run to fixpoint with the object allocation sinking phase. performArgumentsElimination(dfg); if (Options::usePutStackSinking()) performPutStackSinking(dfg); performConstantHoisting(dfg); performGlobalCSE(dfg); performLivenessAnalysis(dfg); performCFA(dfg); performConstantFolding(dfg); performCleanUp(dfg); // Reduce the graph size a lot. changed = false; changed |= performStrengthReduction(dfg); if (Options::useObjectAllocationSinking()) { changed |= performCriticalEdgeBreaking(dfg); changed |= performObjectAllocationSinking(dfg); } if (changed) { // State-at-tail and state-at-head will be invalid if we did strength reduction since // it might increase live ranges. performLivenessAnalysis(dfg); performCFA(dfg); performConstantFolding(dfg); } // Currently, this relies on pre-headers still being valid. That precludes running CFG // simplification before it, unless we re-created the pre-headers. There wouldn't be anything // wrong with running LICM earlier, if we wanted to put other CFG transforms above this point. // Alternatively, we could run loop pre-header creation after SSA conversion - but if we did that // then we'd need to do some simple SSA fix-up. performLivenessAnalysis(dfg); performCFA(dfg); performLICM(dfg); // FIXME: Currently: IntegerRangeOptimization *must* be run after LICM. // // IntegerRangeOptimization makes changes on nodes based on preceding blocks // and nodes. LICM moves nodes which can invalidates assumptions used // by IntegerRangeOptimization. // // Ideally, the dependencies should be explicit. See https://bugs.webkit.org/show_bug.cgi?id=157534. performLivenessAnalysis(dfg); performIntegerRangeOptimization(dfg); performCleanUp(dfg); performIntegerCheckCombining(dfg); performGlobalCSE(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. dfg.m_fixpointState = FixpointConverged; performLivenessAnalysis(dfg); performCFA(dfg); performGlobalStoreBarrierInsertion(dfg); performStoreBarrierClustering(dfg); if (Options::useMovHintRemoval()) performMovHintRemoval(dfg); performCleanUp(dfg); performDCE(dfg); // We rely on this to kill dead code that won't be recognized as dead by B3. performStackLayout(dfg); performLivenessAnalysis(dfg); performOSRAvailabilityAnalysis(dfg); performWatchpointCollection(dfg); if (FTL::canCompile(dfg) == FTL::CannotCompile) { finalizer = std::make_unique<FailedFinalizer>(*this); return FailPath; } dumpAndVerifyGraph(dfg, "Graph just before FTL lowering:", shouldDumpDisassembly(mode)); // Flash a safepoint in case the GC wants some action. Safepoint::Result safepointResult; { GraphSafepoint safepoint(dfg, safepointResult); } if (safepointResult.didGetCancelled()) return CancelPath; FTL::State state(dfg); FTL::lowerDFGToB3(state); if (UNLIKELY(computeCompileTimes())) m_timeBeforeFTL = monotonicallyIncreasingTimeMS(); if (Options::b3AlwaysFailsBeforeCompile()) { FTL::fail(state); return FTLPath; } FTL::compile(state, safepointResult); if (safepointResult.didGetCancelled()) return CancelPath; if (Options::b3AlwaysFailsBeforeLink()) { FTL::fail(state); return FTLPath; } if (state.allocationFailed) { FTL::fail(state); return FTLPath; } FTL::link(state); if (state.allocationFailed) { FTL::fail(state); return FTLPath; } return FTLPath; #else RELEASE_ASSERT_NOT_REACHED(); return FailPath; #endif // ENABLE(FTL_JIT) } default: RELEASE_ASSERT_NOT_REACHED(); return FailPath; } }
inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck, unsigned osrEntryBytecodeIndex) { SamplingRegion samplingRegion("DFG Compilation (Driver)"); numCompilations++; ASSERT(codeBlock); ASSERT(codeBlock->alternative()); ASSERT(codeBlock->alternative()->getJITType() == JITCode::BaselineJIT); ASSERT(osrEntryBytecodeIndex != UINT_MAX); if (!Options::useDFGJIT()) return false; if (!Options::bytecodeRangeToDFGCompile().isInRange(codeBlock->instructionCount())) return false; if (logCompilationChanges()) dataLog("DFG compiling ", *codeBlock, ", number of instructions = ", codeBlock->instructionCount(), "\n"); // Derive our set of must-handle values. The compilation must be at least conservative // enough to allow for OSR entry with these values. unsigned numVarsWithValues; if (osrEntryBytecodeIndex) numVarsWithValues = codeBlock->m_numVars; else numVarsWithValues = 0; Operands<JSValue> mustHandleValues(codeBlock->numParameters(), numVarsWithValues); for (size_t i = 0; i < mustHandleValues.size(); ++i) { int operand = mustHandleValues.operandForIndex(i); if (operandIsArgument(operand) && !operandToArgument(operand) && compileMode == CompileFunction && codeBlock->specializationKind() == CodeForConstruct) { // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will // also never be used. It doesn't matter what we put into the value for this, // but it has to be an actual value that can be grokked by subsequent DFG passes, // so we sanitize it here by turning it into Undefined. mustHandleValues[i] = jsUndefined(); } else mustHandleValues[i] = exec->uncheckedR(operand).jsValue(); } Graph dfg(exec->vm(), codeBlock, osrEntryBytecodeIndex, mustHandleValues); if (!parse(exec, dfg)) return false; // 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); if (validationEnabled()) validate(dfg); performBackwardsPropagation(dfg); performPredictionPropagation(dfg); performFixup(dfg); performTypeCheckHoisting(dfg); dfg.m_fixpointState = FixpointNotConverged; performCSE(dfg); performArgumentsSimplification(dfg); performCPSRethreading(dfg); // This should usually be a no-op since CSE rarely dethreads, and arguments simplification rarely does anything. performCFA(dfg); performConstantFolding(dfg); performCFGSimplification(dfg); dfg.m_fixpointState = FixpointConverged; performStoreElimination(dfg); performCPSRethreading(dfg); performDCE(dfg); performVirtualRegisterAllocation(dfg); GraphDumpMode modeForFinalValidate = DumpGraph; if (verboseCompilationEnabled()) { dataLogF("Graph after optimization:\n"); dfg.dump(); modeForFinalValidate = DontDumpGraph; } if (validationEnabled()) validate(dfg, modeForFinalValidate); JITCompiler dataFlowJIT(dfg); bool result; if (compileMode == CompileFunction) { ASSERT(jitCodeWithArityCheck); result = dataFlowJIT.compileFunction(jitCode, *jitCodeWithArityCheck); } else { ASSERT(compileMode == CompileOther); ASSERT(!jitCodeWithArityCheck); result = dataFlowJIT.compile(jitCode); } return result; }
void NaturalLoops::compute(Graph& graph) { // Implement the classic dominator-based natural loop finder. The first // step is to find all control flow edges A -> B where B dominates A. // Then B is a loop header and A is a backward branching block. We will // then accumulate, for each loop header, multiple backward branching // blocks. Then we backwards graph search from the backward branching // blocks to their loop headers, which gives us all of the blocks in the // loop body. static const bool verbose = false; graph.m_dominators.computeIfNecessary(graph); if (verbose) { dataLog("Dominators:\n"); graph.m_dominators.dump(graph, WTF::dataFile()); } m_loops.resize(0); for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) { BasicBlock* block = graph.block(blockIndex); if (!block) continue; for (unsigned i = block->numSuccessors(); i--;) { BasicBlock* successor = block->successor(i); if (!graph.m_dominators.dominates(successor, block)) continue; bool found = false; for (unsigned j = m_loops.size(); j--;) { if (m_loops[j].header() == successor) { m_loops[j].addBlock(block); found = true; break; } } if (found) continue; NaturalLoop loop(successor, m_loops.size()); loop.addBlock(block); m_loops.append(loop); } } if (verbose) dataLog("After bootstrap: ", *this, "\n"); FastBitVector seenBlocks; Vector<BasicBlock*, 4> blockWorklist; seenBlocks.resize(graph.numBlocks()); for (unsigned i = m_loops.size(); i--;) { NaturalLoop& loop = m_loops[i]; seenBlocks.clearAll(); ASSERT(blockWorklist.isEmpty()); if (verbose) dataLog("Dealing with loop ", loop, "\n"); for (unsigned j = loop.size(); j--;) { seenBlocks.set(loop[j]->index); blockWorklist.append(loop[j]); } while (!blockWorklist.isEmpty()) { BasicBlock* block = blockWorklist.takeLast(); if (verbose) dataLog(" Dealing with ", *block, "\n"); if (block == loop.header()) continue; for (unsigned j = block->predecessors.size(); j--;) { BasicBlock* predecessor = block->predecessors[j]; if (seenBlocks.get(predecessor->index)) continue; loop.addBlock(predecessor); blockWorklist.append(predecessor); seenBlocks.set(predecessor->index); } } } // Figure out reverse mapping from blocks to loops. for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) { BasicBlock* block = graph.block(blockIndex); if (!block) continue; for (unsigned i = BasicBlock::numberOfInnerMostLoopIndices; i--;) block->innerMostLoopIndices[i] = UINT_MAX; } for (unsigned loopIndex = m_loops.size(); loopIndex--;) { NaturalLoop& loop = m_loops[loopIndex]; for (unsigned blockIndexInLoop = loop.size(); blockIndexInLoop--;) { BasicBlock* block = loop[blockIndexInLoop]; for (unsigned i = 0; i < BasicBlock::numberOfInnerMostLoopIndices; ++i) { unsigned thisIndex = block->innerMostLoopIndices[i]; if (thisIndex == UINT_MAX || loop.size() < m_loops[thisIndex].size()) { insertIntoBoundedVector( block->innerMostLoopIndices, BasicBlock::numberOfInnerMostLoopIndices, loopIndex, i); break; } } } } // Now each block knows its inner-most loop and its next-to-inner-most loop. Use // this to figure out loop parenting. for (unsigned i = m_loops.size(); i--;) { NaturalLoop& loop = m_loops[i]; RELEASE_ASSERT(loop.header()->innerMostLoopIndices[0] == i); loop.m_outerLoopIndex = loop.header()->innerMostLoopIndices[1]; } if (validationEnabled()) { // Do some self-verification that we've done some of this correctly. for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) { BasicBlock* block = graph.block(blockIndex); if (!block) continue; Vector<const NaturalLoop*> simpleLoopsOf; for (unsigned i = m_loops.size(); i--;) { if (m_loops[i].contains(block)) simpleLoopsOf.append(&m_loops[i]); } Vector<const NaturalLoop*> fancyLoopsOf = loopsOf(block); std::sort(simpleLoopsOf.begin(), simpleLoopsOf.end()); std::sort(fancyLoopsOf.begin(), fancyLoopsOf.end()); RELEASE_ASSERT(simpleLoopsOf == fancyLoopsOf); } } if (verbose) dataLog("Results: ", *this, "\n"); }
Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState) { 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); if (validationEnabled()) validate(dfg); performBackwardsPropagation(dfg); performPredictionPropagation(dfg); performFixup(dfg); performTypeCheckHoisting(dfg); unsigned count = 1; dfg.m_fixpointState = FixpointNotConverged; for (;; ++count) { if (logCompilationChanges()) dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", count); bool changed = false; if (validationEnabled()) validate(dfg); performCFA(dfg); changed |= performConstantFolding(dfg); changed |= performArgumentsSimplification(dfg); changed |= performCFGSimplification(dfg); changed |= performCSE(dfg); if (!changed) break; performCPSRethreading(dfg); } if (logCompilationChanges()) dataLogF("DFG optimization fixpoint converged in %u iterations.\n", count); dfg.m_fixpointState = FixpointConverged; performStoreElimination(dfg); // 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); } #if ENABLE(FTL_JIT) if (Options::useExperimentalFTL() && compileMode == CompileFunction && FTL::canCompile(dfg)) { performCriticalEdgeBreaking(dfg); performLoopPreHeaderCreation(dfg); performCPSRethreading(dfg); performSSAConversion(dfg); performLivenessAnalysis(dfg); performCFA(dfg); performLICM(dfg); performLivenessAnalysis(dfg); performCFA(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. performLivenessAnalysis(dfg); performFlushLivenessAnalysis(dfg); performOSRAvailabilityAnalysis(dfg); dumpAndVerifyGraph(dfg, "Graph just before FTL lowering:"); // FIXME: Support OSR entry. // https://bugs.webkit.org/show_bug.cgi?id=113625 FTL::State state(dfg); FTL::lowerDFGToLLVM(state); if (Options::reportCompileTimes()) beforeFTL = currentTimeMS(); if (Options::llvmAlwaysFails()) { FTL::fail(state); return FTLPath; } FTL::compile(state); FTL::link(state); return FTLPath; } #endif // ENABLE(FTL_JIT) performCPSRethreading(dfg); performDCE(dfg); performVirtualRegisterAllocation(dfg); dumpAndVerifyGraph(dfg, "Graph after optimization:"); JITCompiler dataFlowJIT(dfg); if (compileMode == CompileFunction) { dataFlowJIT.compileFunction(); dataFlowJIT.linkFunction(); } else { ASSERT(compileMode == CompileOther); dataFlowJIT.compile(); dataFlowJIT.link(); } return DFGPath; }