| /* |
| * Copyright (C) 2021 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 "JITPlan.h" |
| |
| #if ENABLE(JIT) |
| |
| #include "AbstractSlotVisitor.h" |
| #include "CodeBlock.h" |
| #include "HeapInlines.h" |
| #include "VMInlines.h" |
| #include <wtf/CompilationThread.h> |
| |
| namespace JSC { |
| |
| extern Seconds totalBaselineCompileTime; |
| extern Seconds totalDFGCompileTime; |
| extern Seconds totalFTLCompileTime; |
| extern Seconds totalFTLDFGCompileTime; |
| extern Seconds totalFTLB3CompileTime; |
| |
| JITPlan::JITPlan(JITCompilationMode mode, CodeBlock* codeBlock) |
| : m_mode(mode) |
| , m_vm(&codeBlock->vm()) |
| , m_codeBlock(codeBlock) |
| { |
| } |
| |
| void JITPlan::cancel() |
| { |
| RELEASE_ASSERT(m_stage != JITPlanStage::Canceled); |
| ASSERT(m_vm); |
| m_stage = JITPlanStage::Canceled; |
| m_vm = nullptr; |
| m_codeBlock = nullptr; |
| } |
| |
| void JITPlan::notifyCompiling() |
| { |
| m_stage = JITPlanStage::Compiling; |
| } |
| |
| void JITPlan::notifyReady() |
| { |
| m_stage = JITPlanStage::Ready; |
| } |
| |
| auto JITPlan::tier() const -> Tier |
| { |
| switch (m_mode) { |
| case JITCompilationMode::InvalidCompilation: |
| RELEASE_ASSERT_NOT_REACHED(); |
| return Tier::Baseline; |
| case JITCompilationMode::Baseline: |
| return Tier::Baseline; |
| case JITCompilationMode::DFG: |
| return Tier::DFG; |
| case JITCompilationMode::FTL: |
| case JITCompilationMode::FTLForOSREntry: |
| return Tier::FTL; |
| } |
| RELEASE_ASSERT_NOT_REACHED(); |
| } |
| |
| JITCompilationKey JITPlan::key() |
| { |
| JSCell* codeBlock; |
| if (m_mode == JITCompilationMode::Baseline) |
| codeBlock = m_codeBlock->unlinkedCodeBlock(); |
| else |
| codeBlock = m_codeBlock->baselineAlternative(); |
| return JITCompilationKey(codeBlock, m_mode); |
| } |
| |
| bool JITPlan::isKnownToBeLiveAfterGC() |
| { |
| if (m_stage == JITPlanStage::Canceled) |
| return false; |
| if (!m_vm->heap.isMarked(m_codeBlock->ownerExecutable())) |
| return false; |
| return true; |
| } |
| |
| bool JITPlan::isKnownToBeLiveDuringGC(AbstractSlotVisitor& visitor) |
| { |
| if (m_stage == JITPlanStage::Canceled) |
| return false; |
| if (!visitor.isMarked(m_codeBlock->ownerExecutable())) |
| return false; |
| return true; |
| } |
| |
| bool JITPlan::iterateCodeBlocksForGC(AbstractSlotVisitor& visitor, const Function<void(CodeBlock*)>& func) |
| { |
| if (!isKnownToBeLiveDuringGC(visitor)) |
| return false; |
| |
| // Compilation writes lots of values to a CodeBlock without performing |
| // an explicit barrier. So, we need to be pessimistic and assume that |
| // all our CodeBlocks must be visited during GC. |
| func(m_codeBlock); |
| return true; |
| } |
| |
| bool JITPlan::checkLivenessAndVisitChildren(AbstractSlotVisitor& visitor) |
| { |
| if (!isKnownToBeLiveDuringGC(visitor)) |
| return false; |
| |
| visitor.appendUnbarriered(m_codeBlock); |
| return true; |
| } |
| |
| bool JITPlan::computeCompileTimes() const |
| { |
| return reportCompileTimes() |
| || Options::reportTotalCompileTimes() |
| || (m_vm && m_vm->m_perBytecodeProfiler); |
| } |
| |
| bool JITPlan::reportCompileTimes() const |
| { |
| return Options::reportCompileTimes() |
| || (Options::reportBaselineCompileTimes() && m_mode == JITCompilationMode::Baseline) |
| || (Options::reportDFGCompileTimes() && m_mode == JITCompilationMode::DFG) |
| || (Options::reportFTLCompileTimes() && isFTL()); |
| } |
| |
| void JITPlan::compileInThread(JITWorklistThread* thread) |
| { |
| m_thread = thread; |
| |
| MonotonicTime before; |
| CString codeBlockName; |
| if (UNLIKELY(computeCompileTimes())) |
| before = MonotonicTime::now(); |
| if (UNLIKELY(reportCompileTimes())) |
| codeBlockName = toCString(*m_codeBlock); |
| |
| CompilationScope compilationScope; |
| |
| #if ENABLE(DFG_JIT) |
| if (DFG::logCompilationChanges(m_mode) || Options::logPhaseTimes()) |
| dataLog("DFG(Plan) compiling ", *m_codeBlock, " with ", m_mode, ", instructions size = ", m_codeBlock->instructionsSize(), "\n"); |
| #endif // ENABLE(DFG_JIT) |
| |
| CompilationPath path = compileInThreadImpl(); |
| |
| RELEASE_ASSERT((path == CancelPath) == (m_stage == JITPlanStage::Canceled)); |
| |
| MonotonicTime after; |
| if (UNLIKELY(computeCompileTimes())) { |
| after = MonotonicTime::now(); |
| |
| if (Options::reportTotalCompileTimes()) { |
| if (isFTL()) { |
| totalFTLCompileTime += after - before; |
| totalFTLDFGCompileTime += m_timeBeforeFTL - before; |
| totalFTLB3CompileTime += after - m_timeBeforeFTL; |
| } else if (mode() == JITCompilationMode::Baseline) |
| totalBaselineCompileTime += after - before; |
| else |
| totalDFGCompileTime += after - before; |
| } |
| } |
| const char* pathName = nullptr; |
| switch (path) { |
| case FailPath: |
| pathName = "N/A (fail)"; |
| break; |
| case BaselinePath: |
| pathName = "Baseline"; |
| break; |
| case DFGPath: |
| pathName = "DFG"; |
| break; |
| case FTLPath: |
| pathName = "FTL"; |
| break; |
| case CancelPath: |
| pathName = "Canceled"; |
| break; |
| default: |
| RELEASE_ASSERT_NOT_REACHED(); |
| break; |
| } |
| if (m_codeBlock) { // m_codeBlock will be null if the compilation was cancelled. |
| if (path == FTLPath) |
| CODEBLOCK_LOG_EVENT(m_codeBlock, "ftlCompile", ("took ", (after - before).milliseconds(), " ms (DFG: ", (m_timeBeforeFTL - before).milliseconds(), ", B3: ", (after - m_timeBeforeFTL).milliseconds(), ") with ", pathName)); |
| else |
| CODEBLOCK_LOG_EVENT(m_codeBlock, "dfgCompile", ("took ", (after - before).milliseconds(), " ms with ", pathName)); |
| } |
| if (UNLIKELY(reportCompileTimes())) { |
| dataLog("Optimized ", codeBlockName, " using ", m_mode, " with ", pathName, " into ", codeSize(), " bytes in ", (after - before).milliseconds(), " ms"); |
| if (path == FTLPath) |
| dataLog(" (DFG: ", (m_timeBeforeFTL - before).milliseconds(), ", B3: ", (after - m_timeBeforeFTL).milliseconds(), ")"); |
| dataLog(".\n"); |
| } |
| } |
| |
| } // namespace JSC |
| |
| #endif // ENABLE(DFG_JIT) |