/ bytecode / DFGExitProfile.h
DFGExitProfile.h
  1  /*
  2   * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 24   */
 25  
 26  #pragma once
 27  
 28  #if ENABLE(DFG_JIT)
 29  
 30  #include "ConcurrentJSLock.h"
 31  #include "ExitKind.h"
 32  #include "ExitingInlineKind.h"
 33  #include "ExitingJITType.h"
 34  #include <wtf/HashSet.h>
 35  #include <wtf/Vector.h>
 36  
 37  namespace JSC { namespace DFG {
 38  
 39  class FrequentExitSite {
 40  public:
 41      FrequentExitSite()
 42          : m_bytecodeIndex(BytecodeIndex(0))
 43          , m_kind(ExitKindUnset)
 44          , m_jitType(ExitFromAnything)
 45          , m_inlineKind(ExitFromAnyInlineKind)
 46      {
 47      }
 48      
 49      FrequentExitSite(WTF::HashTableDeletedValueType)
 50          : m_bytecodeIndex(WTF::HashTableDeletedValue)
 51          , m_kind(ExitKindUnset)
 52          , m_jitType(ExitFromAnything)
 53          , m_inlineKind(ExitFromAnyInlineKind)
 54      {
 55      }
 56      
 57      explicit FrequentExitSite(BytecodeIndex bytecodeIndex, ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind)
 58          : m_bytecodeIndex(bytecodeIndex)
 59          , m_kind(kind)
 60          , m_jitType(jitType)
 61          , m_inlineKind(inlineKind)
 62      {
 63          if (m_kind == ArgumentsEscaped) {
 64              // Count this one globally. It doesn't matter where in the code block the arguments excaped;
 65              // the fact that they did is not associated with any particular instruction.
 66              m_bytecodeIndex = BytecodeIndex(0);
 67          }
 68      }
 69      
 70      // Use this constructor if you wish for the exit site to be counted globally within its
 71      // code block.
 72      explicit FrequentExitSite(ExitKind kind, ExitingJITType jitType = ExitFromAnything, ExitingInlineKind inlineKind = ExitFromAnyInlineKind)
 73          : m_bytecodeIndex(BytecodeIndex(0))
 74          , m_kind(kind)
 75          , m_jitType(jitType)
 76          , m_inlineKind(inlineKind)
 77      {
 78      }
 79      
 80      bool operator!() const
 81      {
 82          return m_kind == ExitKindUnset;
 83      }
 84      
 85      bool operator==(const FrequentExitSite& other) const
 86      {
 87          return m_bytecodeIndex == other.m_bytecodeIndex
 88              && m_kind == other.m_kind
 89              && m_jitType == other.m_jitType
 90              && m_inlineKind == other.m_inlineKind;
 91      }
 92      
 93      bool subsumes(const FrequentExitSite& other) const
 94      {
 95          if (m_bytecodeIndex != other.m_bytecodeIndex)
 96              return false;
 97          if (m_kind != other.m_kind)
 98              return false;
 99          if (m_jitType != ExitFromAnything
100              && m_jitType != other.m_jitType)
101              return false;
102          if (m_inlineKind != ExitFromAnyInlineKind
103              && m_inlineKind != other.m_inlineKind)
104              return false;
105          return true;
106      }
107      
108      unsigned hash() const
109      {
110          return m_bytecodeIndex.hash() + m_kind + static_cast<unsigned>(m_jitType) * 7 + static_cast<unsigned>(m_inlineKind) * 11;
111      }
112      
113      BytecodeIndex bytecodeIndex() const { return m_bytecodeIndex; }
114      ExitKind kind() const { return m_kind; }
115      ExitingJITType jitType() const { return m_jitType; }
116      ExitingInlineKind inlineKind() const { return m_inlineKind; }
117      
118      FrequentExitSite withJITType(ExitingJITType jitType) const
119      {
120          FrequentExitSite result = *this;
121          result.m_jitType = jitType;
122          return result;
123      }
124  
125      FrequentExitSite withInlineKind(ExitingInlineKind inlineKind) const
126      {
127          FrequentExitSite result = *this;
128          result.m_inlineKind = inlineKind;
129          return result;
130      }
131  
132      bool isHashTableDeletedValue() const
133      {
134          return m_kind == ExitKindUnset && m_bytecodeIndex.isHashTableDeletedValue();
135      }
136      
137      void dump(PrintStream& out) const;
138  
139  private:
140      BytecodeIndex m_bytecodeIndex;
141      ExitKind m_kind;
142      ExitingJITType m_jitType;
143      ExitingInlineKind m_inlineKind;
144  };
145  
146  struct FrequentExitSiteHash {
147      static unsigned hash(const FrequentExitSite& key) { return key.hash(); }
148      static bool equal(const FrequentExitSite& a, const FrequentExitSite& b) { return a == b; }
149      static constexpr bool safeToCompareToEmptyOrDeleted = true;
150  };
151  
152  } } // namespace JSC::DFG
153  
154  
155  namespace WTF {
156  
157  template<typename T> struct DefaultHash;
158  template<> struct DefaultHash<JSC::DFG::FrequentExitSite> : JSC::DFG::FrequentExitSiteHash { };
159  
160  template<typename T> struct HashTraits;
161  template<> struct HashTraits<JSC::DFG::FrequentExitSite> : SimpleClassHashTraits<JSC::DFG::FrequentExitSite> { };
162  
163  } // namespace WTF
164  
165  namespace JSC { namespace DFG {
166  
167  class QueryableExitProfile;
168  
169  class ExitProfile {
170  public:
171      ExitProfile();
172      ~ExitProfile();
173      
174      // Add a new frequent exit site. Return true if this is a new one, or false
175      // if we already knew about it. This is an O(n) operation, because it errs
176      // on the side of keeping the data structure compact. Also, this will only
177      // be called a fixed number of times per recompilation. Recompilation is
178      // rare to begin with, and implies doing O(n) operations on the CodeBlock
179      // anyway.
180      static bool add(CodeBlock*, const FrequentExitSite&);
181      
182      // Get the frequent exit sites for a bytecode index. This is O(n), and is
183      // meant to only be used from debugging/profiling code.
184      Vector<FrequentExitSite> exitSitesFor(BytecodeIndex);
185      
186      // This is O(n) and should be called on less-frequently executed code paths
187      // in the compiler. It should be strictly cheaper than building a
188      // QueryableExitProfile, if you really expect this to be called infrequently
189      // and you believe that there are few exit sites.
190      bool hasExitSite(const ConcurrentJSLocker&, const FrequentExitSite&) const;
191      bool hasExitSite(const ConcurrentJSLocker& locker, ExitKind kind) const
192      {
193          return hasExitSite(locker, FrequentExitSite(kind));
194      }
195      
196  private:
197      friend class QueryableExitProfile;
198      
199      std::unique_ptr<Vector<FrequentExitSite>> m_frequentExitSites;
200  };
201  
202  class QueryableExitProfile {
203  public:
204      QueryableExitProfile();
205      ~QueryableExitProfile();
206      
207      void initialize(UnlinkedCodeBlock*);
208  
209      bool hasExitSite(const FrequentExitSite& site) const
210      {
211          if (site.jitType() == ExitFromAnything) {
212              return hasExitSiteWithSpecificJITType(site.withJITType(ExitFromDFG))
213                  || hasExitSiteWithSpecificJITType(site.withJITType(ExitFromFTL));
214          }
215          return hasExitSiteWithSpecificJITType(site);
216      }
217      
218      bool hasExitSite(ExitKind kind) const
219      {
220          return hasExitSite(FrequentExitSite(kind));
221      }
222      
223      bool hasExitSite(BytecodeIndex bytecodeIndex, ExitKind kind) const
224      {
225          return hasExitSite(FrequentExitSite(bytecodeIndex, kind));
226      }
227  private:
228      bool hasExitSiteWithSpecificJITType(const FrequentExitSite& site) const
229      {
230          if (site.inlineKind() == ExitFromAnyInlineKind) {
231              return hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromNotInlined))
232                  || hasExitSiteWithSpecificInlineKind(site.withInlineKind(ExitFromInlined));
233          }
234          return hasExitSiteWithSpecificInlineKind(site);
235      }
236      
237      bool hasExitSiteWithSpecificInlineKind(const FrequentExitSite& site) const
238      {
239          return m_frequentExitSites.find(site) != m_frequentExitSites.end();
240      }
241      
242      HashSet<FrequentExitSite> m_frequentExitSites;
243  };
244  
245  } } // namespace JSC::DFG
246  
247  #endif // ENABLE(DFG_JIT)