static CompilationResult compileImpl( VM& vm, CodeBlock* codeBlock, CodeBlock* profiledDFGCodeBlock, CompilationMode mode, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues, PassRefPtr<DeferredCompilationCallback> callback) { SamplingRegion samplingRegion("DFG Compilation (Driver)"); numCompilations++; ASSERT(codeBlock); ASSERT(codeBlock->alternative()); ASSERT(codeBlock->alternative()->jitType() == JITCode::BaselineJIT); ASSERT(!profiledDFGCodeBlock || profiledDFGCodeBlock->jitType() == JITCode::DFGJIT); if (logCompilationChanges(mode)) dataLog("DFG(Driver) compiling ", *codeBlock, " with ", mode, ", number of instructions = ", codeBlock->instructionCount(), "\n"); // Make sure that any stubs that the DFG is going to use are initialized. We want to // make sure that all JIT code generation does finalization on the main thread. vm.getCTIStub(osrExitGenerationThunkGenerator); vm.getCTIStub(throwExceptionFromCallSlowPathGenerator); if (mode == DFGMode) { vm.getCTIStub(linkCallThunkGenerator); vm.getCTIStub(linkConstructThunkGenerator); vm.getCTIStub(linkClosureCallThunkGenerator); vm.getCTIStub(virtualCallThunkGenerator); vm.getCTIStub(virtualConstructThunkGenerator); } else { vm.getCTIStub(linkCallThatPreservesRegsThunkGenerator); vm.getCTIStub(linkConstructThatPreservesRegsThunkGenerator); vm.getCTIStub(linkClosureCallThatPreservesRegsThunkGenerator); vm.getCTIStub(virtualCallThatPreservesRegsThunkGenerator); vm.getCTIStub(virtualConstructThatPreservesRegsThunkGenerator); } if (CallEdgeLog::isEnabled()) vm.ensureCallEdgeLog().processLog(); if (vm.typeProfiler()) vm.typeProfilerLog()->processLogEntries(ASCIILiteral("Preparing for DFG compilation.")); RefPtr<Plan> plan = adoptRef( new Plan(codeBlock, profiledDFGCodeBlock, mode, osrEntryBytecodeIndex, mustHandleValues)); if (Options::enableConcurrentJIT()) { Worklist* worklist = ensureGlobalWorklistFor(mode); plan->callback = callback; if (logCompilationChanges(mode)) dataLog("Deferring DFG compilation of ", *codeBlock, " with queue length ", worklist->queueLength(), ".\n"); worklist->enqueue(plan); return CompilationDeferred; } plan->compileInThread(*vm.dfgState, 0); return plan->finalizeWithoutNotifyingCallback(); }
void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadData) { this->threadData = threadData; double before = 0; if (reportCompileTimes()) before = currentTimeMS(); 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(finalizer); if (reportCompileTimes()) { const char* pathName; switch (path) { case FailPath: pathName = "N/A (fail)"; break; case DFGPath: pathName = "DFG"; break; case FTLPath: pathName = "FTL"; break; default: RELEASE_ASSERT_NOT_REACHED(); pathName = ""; break; } double now = currentTimeMS(); dataLog("Optimized ", *codeBlock, " using ", mode, " with ", pathName, " into ", finalizer->codeSize(), " 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() && 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; }
void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadData) { this->threadData = threadData; double before = 0; CString codeBlockName; if (UNLIKELY(computeCompileTimes())) before = monotonicallyIncreasingTimeMS(); if (UNLIKELY(reportCompileTimes())) codeBlockName = toCString(*codeBlock); CompilationScope compilationScope; if (logCompilationChanges(mode) || Options::reportDFGPhaseTimes()) 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)); double after = 0; if (UNLIKELY(computeCompileTimes())) { after = monotonicallyIncreasingTimeMS(); if (Options::reportTotalCompileTimes()) { if (isFTL(mode)) { totalFTLCompileTime += after - before; totalFTLDFGCompileTime += m_timeBeforeFTL - before; totalFTLB3CompileTime += after - m_timeBeforeFTL; } else totalDFGCompileTime += after - before; } } const char* pathName = nullptr; 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(); break; } if (codeBlock) { // codeBlock will be null if the compilation was cancelled. if (path == FTLPath) CODEBLOCK_LOG_EVENT(codeBlock, "ftlCompile", ("took ", after - before, " ms (DFG: ", m_timeBeforeFTL - before, ", B3: ", after - m_timeBeforeFTL, ") with ", pathName)); else CODEBLOCK_LOG_EVENT(codeBlock, "dfgCompile", ("took ", after - before, " ms with ", pathName)); } if (UNLIKELY(reportCompileTimes())) { dataLog("Optimized ", codeBlockName, " using ", mode, " with ", pathName, " into ", finalizer ? finalizer->codeSize() : 0, " bytes in ", after - before, " ms"); if (path == FTLPath) dataLog(" (DFG: ", m_timeBeforeFTL - before, ", B3: ", after - m_timeBeforeFTL, ")"); dataLog(".\n"); } }
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; }
static CompilationResult compileImpl( ExecState* exec, CodeBlock* codeBlock, CompilationMode mode, unsigned osrEntryBytecodeIndex, PassRefPtr<DeferredCompilationCallback> callback, Worklist* worklist) { SamplingRegion samplingRegion("DFG Compilation (Driver)"); numCompilations++; ASSERT(codeBlock); ASSERT(codeBlock->alternative()); ASSERT(codeBlock->alternative()->jitType() == JITCode::BaselineJIT); ASSERT(osrEntryBytecodeIndex != UINT_MAX); if (!Options::useDFGJIT() || !MacroAssembler::supportsFloatingPoint()) return CompilationFailed; if (!Options::bytecodeRangeToDFGCompile().isInRange(codeBlock->instructionCount())) return CompilationFailed; if (logCompilationChanges()) dataLog("DFG(Driver) compiling ", *codeBlock, ", number of instructions = ", codeBlock->instructionCount(), "\n"); VM& vm = exec->vm(); // Make sure that any stubs that the DFG is going to use are initialized. We want to // make sure that al JIT code generation does finalization on the main thread. vm.getCTIStub(osrExitGenerationThunkGenerator); vm.getCTIStub(throwExceptionFromCallSlowPathGenerator); vm.getCTIStub(linkCallThunkGenerator); vm.getCTIStub(linkConstructThunkGenerator); vm.getCTIStub(linkClosureCallThunkGenerator); vm.getCTIStub(virtualCallThunkGenerator); vm.getCTIStub(virtualConstructThunkGenerator); #if ENABLE(FTL_JIT) vm.getCTIStub(FTL::osrExitGenerationThunkGenerator); #endif // 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; RefPtr<Plan> plan = adoptRef( new Plan(codeBlock, mode, osrEntryBytecodeIndex, numVarsWithValues)); for (size_t i = 0; i < plan->mustHandleValues.size(); ++i) { int operand = plan->mustHandleValues.operandForIndex(i); if (operandIsArgument(operand) && !operandToArgument(operand) && codeBlock->codeType() == FunctionCode && 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. plan->mustHandleValues[i] = jsUndefined(); } else plan->mustHandleValues[i] = exec->uncheckedR(operand).jsValue(); } if (worklist) { plan->callback = callback; if (logCompilationChanges()) dataLog("Deferring DFG compilation of ", *codeBlock, " with queue length ", worklist->queueLength(), ".\n"); worklist->enqueue(plan); return CompilationDeferred; } plan->compileInThread(*vm.dfgState); return plan->finalizeWithoutNotifyingCallback(); }
void Plan::compileInThread(LongLivedState& longLivedState, ThreadData* threadData) { this->threadData = threadData; double before = 0; CString codeBlockName; if (computeCompileTimes()) before = monotonicallyIncreasingTimeMS(); if (reportCompileTimes()) 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)); double after = 0; if (computeCompileTimes()) after = monotonicallyIncreasingTimeMS(); if (Options::reportTotalCompileTimes()) { if (isFTL(mode)) { totalFTLCompileTime += after - before; totalFTLDFGCompileTime += m_timeBeforeFTL - before; totalFTLB3CompileTime += after - m_timeBeforeFTL; } else totalDFGCompileTime += after - before; } 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(); #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE) pathName = ""; #endif break; } dataLog("Optimized ", codeBlockName, " using ", mode, " with ", pathName, " into ", finalizer ? finalizer->codeSize() : 0, " bytes in ", after - before, " ms"); if (path == FTLPath) dataLog(" (DFG: ", m_timeBeforeFTL - before, ", B3: ", after - m_timeBeforeFTL, ")"); dataLog(".\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; }