|  | /* | 
|  | * Copyright (C) 2011-2019 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. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "ConcurrentJSLock.h" | 
|  | #include "ExitKind.h" | 
|  | #include "ExitingInlineKind.h" | 
|  | #include "ExitingJITType.h" | 
|  | #include <wtf/HashSet.h> | 
|  | #include <wtf/Vector.h> | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | class FrequentExitSite { | 
|  | public: | 
|  | FrequentExitSite() | 
|  | : m_bytecodeIndex(BytecodeIndex(0)) | 
|  | , m_kind(ExitKindUnset) | 
|  | , m_jitType(ExitFromAnything) | 
|  | , m_inlineKind(ExitFromAnyInlineKind) | 
|  | { | 
|  | } | 
|  |  | 
|  | FrequentExitSite(WTF::HashTableDeletedValueType) | 
|  | : m_bytecodeIndex(WTF::HashTableDeletedValue) | 
|  | , m_kind(ExitKindUnset) | 
|  | , m_jitType(ExitFromAnything) | 
|  | , m_inlineKind(ExitFromAnyInlineKind) | 
|  | { | 
|  | } | 
|  |  | 
|  | explicit FrequentExitSite(BytecodeIndex bytecodeIndex, ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind) | 
|  | : m_bytecodeIndex(bytecodeIndex) | 
|  | , m_kind(kind) | 
|  | , m_jitType(jitType) | 
|  | , m_inlineKind(inlineKind) | 
|  | { | 
|  | if (m_kind == ArgumentsEscaped) { | 
|  | // Count this one globally. It doesn't matter where in the code block the arguments excaped; | 
|  | // the fact that they did is not associated with any particular instruction. | 
|  | m_bytecodeIndex = BytecodeIndex(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Use this constructor if you wish for the exit site to be counted globally within its | 
|  | // code block. | 
|  | explicit FrequentExitSite(ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind) | 
|  | : m_bytecodeIndex(BytecodeIndex(0)) | 
|  | , m_kind(kind) | 
|  | , m_jitType(jitType) | 
|  | , m_inlineKind(inlineKind) | 
|  | { | 
|  | } | 
|  |  | 
|  | bool operator!() const | 
|  | { | 
|  | return m_kind == ExitKindUnset; | 
|  | } | 
|  |  | 
|  | bool operator==(const FrequentExitSite& other) const | 
|  | { | 
|  | return m_bytecodeIndex == other.m_bytecodeIndex | 
|  | && m_kind == other.m_kind | 
|  | && m_jitType == other.m_jitType | 
|  | && m_inlineKind == other.m_inlineKind; | 
|  | } | 
|  |  | 
|  | bool subsumes(const FrequentExitSite& other) const | 
|  | { | 
|  | if (m_bytecodeIndex != other.m_bytecodeIndex) | 
|  | return false; | 
|  | if (m_kind != other.m_kind) | 
|  | return false; | 
|  | if (m_jitType != ExitFromAnything | 
|  | && m_jitType != other.m_jitType) | 
|  | return false; | 
|  | if (m_inlineKind != ExitFromAnyInlineKind | 
|  | && m_inlineKind != other.m_inlineKind) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | unsigned hash() const | 
|  | { | 
|  | return m_bytecodeIndex.hash() + m_kind + static_cast<unsigned>(m_jitType) * 7 + static_cast<unsigned>(m_inlineKind) * 11; | 
|  | } | 
|  |  | 
|  | BytecodeIndex bytecodeIndex() const { return m_bytecodeIndex; } | 
|  | ExitKind kind() const { return m_kind; } | 
|  | ExitingJITType jitType() const { return m_jitType; } | 
|  | ExitingInlineKind inlineKind() const { return m_inlineKind; } | 
|  |  | 
|  | FrequentExitSite withJITType(ExitingJITType jitType) const | 
|  | { | 
|  | FrequentExitSite result = *this; | 
|  | result.m_jitType = jitType; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | FrequentExitSite withInlineKind(ExitingInlineKind inlineKind) const | 
|  | { | 
|  | FrequentExitSite result = *this; | 
|  | result.m_inlineKind = inlineKind; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool isHashTableDeletedValue() const | 
|  | { | 
|  | return m_kind == ExitKindUnset && m_bytecodeIndex.isHashTableDeletedValue(); | 
|  | } | 
|  |  | 
|  | void dump(PrintStream& out) const; | 
|  |  | 
|  | private: | 
|  | BytecodeIndex m_bytecodeIndex; | 
|  | ExitKind m_kind; | 
|  | ExitingJITType m_jitType; | 
|  | ExitingInlineKind m_inlineKind; | 
|  | }; | 
|  |  | 
|  | struct FrequentExitSiteHash { | 
|  | static unsigned hash(const FrequentExitSite& key) { return key.hash(); } | 
|  | static bool equal(const FrequentExitSite& a, const FrequentExitSite& b) { return a == b; } | 
|  | static constexpr bool safeToCompareToEmptyOrDeleted = true; | 
|  | }; | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  |  | 
|  | namespace WTF { | 
|  |  | 
|  | template<typename T> struct DefaultHash; | 
|  | template<> struct DefaultHash<JSC::DFG::FrequentExitSite> : JSC::DFG::FrequentExitSiteHash { }; | 
|  |  | 
|  | template<typename T> struct HashTraits; | 
|  | template<> struct HashTraits<JSC::DFG::FrequentExitSite> : SimpleClassHashTraits<JSC::DFG::FrequentExitSite> { }; | 
|  |  | 
|  | } // namespace WTF | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | class QueryableExitProfile; | 
|  |  | 
|  | class ExitProfile { | 
|  | public: | 
|  | ExitProfile(); | 
|  | ~ExitProfile(); | 
|  |  | 
|  | // Add a new frequent exit site. Return true if this is a new one, or false | 
|  | // if we already knew about it. This is an O(n) operation, because it errs | 
|  | // on the side of keeping the data structure compact. Also, this will only | 
|  | // be called a fixed number of times per recompilation. Recompilation is | 
|  | // rare to begin with, and implies doing O(n) operations on the CodeBlock | 
|  | // anyway. | 
|  | static bool add(CodeBlock*, const FrequentExitSite&); | 
|  |  | 
|  | // Get the frequent exit sites for a bytecode index. This is O(n), and is | 
|  | // meant to only be used from debugging/profiling code. | 
|  | Vector<FrequentExitSite> exitSitesFor(BytecodeIndex); | 
|  |  | 
|  | // This is O(n) and should be called on less-frequently executed code paths | 
|  | // in the compiler. It should be strictly cheaper than building a | 
|  | // QueryableExitProfile, if you really expect this to be called infrequently | 
|  | // and you believe that there are few exit sites. | 
|  | bool hasExitSite(const ConcurrentJSLocker&, const FrequentExitSite&) const; | 
|  | bool hasExitSite(const ConcurrentJSLocker& locker, ExitKind kind) const | 
|  | { | 
|  | return hasExitSite(locker, FrequentExitSite(kind)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class QueryableExitProfile; | 
|  |  | 
|  | std::unique_ptr<Vector<FrequentExitSite>> m_frequentExitSites; | 
|  | }; | 
|  |  | 
|  | class QueryableExitProfile { | 
|  | public: | 
|  | QueryableExitProfile(); | 
|  | ~QueryableExitProfile(); | 
|  |  | 
|  | void initialize(UnlinkedCodeBlock*); | 
|  |  | 
|  | bool hasExitSite(const FrequentExitSite& site) const | 
|  | { | 
|  | if (site.jitType() == ExitFromAnything) { | 
|  | return hasExitSiteWithSpecificJITType(site.withJITType(ExitFromDFG)) | 
|  | || hasExitSiteWithSpecificJITType(site.withJITType(ExitFromFTL)); | 
|  | } | 
|  | return hasExitSiteWithSpecificJITType(site); | 
|  | } | 
|  |  | 
|  | bool hasExitSite(ExitKind kind) const | 
|  | { | 
|  | return hasExitSite(FrequentExitSite(kind)); | 
|  | } | 
|  |  | 
|  | bool hasExitSite(BytecodeIndex bytecodeIndex, ExitKind kind) const | 
|  | { | 
|  | return hasExitSite(FrequentExitSite(bytecodeIndex, kind)); | 
|  | } | 
|  | private: | 
|  | bool hasExitSiteWithSpecificJITType(const FrequentExitSite& site) const | 
|  | { | 
|  | if (site.inlineKind() == ExitFromAnyInlineKind) { | 
|  | return hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromNotInlined)) | 
|  | || hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromInlined)); | 
|  | } | 
|  | return hasExitSiteWithSpecificInlineKind(site); | 
|  | } | 
|  |  | 
|  | bool hasExitSiteWithSpecificInlineKind(const FrequentExitSite& site) const | 
|  | { | 
|  | return m_frequentExitSites.find(site) != m_frequentExitSites.end(); | 
|  | } | 
|  |  | 
|  | HashSet<FrequentExitSite> m_frequentExitSites; | 
|  | }; | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | #endif // ENABLE(DFG_JIT) |