tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ICState.h (7449B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef jit_ICState_h
      8 #define jit_ICState_h
      9 
     10 #include "jit/JitOptions.h"
     11 
     12 namespace js {
     13 namespace jit {
     14 
     15 // Used to track trial inlining status for a Baseline IC.
     16 // See also setTrialInliningState below.
     17 enum class TrialInliningState : uint8_t {
     18  Initial = 0,
     19  Candidate,
     20  Inlined,
     21  MonomorphicInlined,
     22  Failure,
     23 };
     24 
     25 // ICState stores information about a Baseline or Ion IC.
     26 class ICState {
     27 public:
     28  // When we attach the maximum number of stubs, we discard all stubs and
     29  // transition the IC to Megamorphic to attach stubs that are more generic
     30  // (handle more cases). If we again attach the maximum number of stubs, we
     31  // transition to Generic and (depending on the IC) will either attach a
     32  // single stub that handles everything or stop attaching new stubs.
     33  //
     34  // We also transition to Generic when we repeatedly fail to attach a stub,
     35  // to avoid wasting time trying.
     36  enum class Mode : uint8_t { Specialized = 0, Megamorphic, Generic };
     37 
     38 private:
     39  uint8_t mode_ : 2;
     40 
     41  // The TrialInliningState for a Baseline IC.
     42  uint8_t trialInliningState_ : 3;
     43 
     44  // Whether WarpOracle created a snapshot based on stubs attached to this
     45  // Baseline IC.
     46  bool usedByTranspiler_ : 1;
     47 
     48  // Whether stubs attached to this IC have been folded together into a single
     49  // stub. Used as a hint when attaching additional stubs to try folding them
     50  // too. The folded stub may be removed later by GC sweeping so this is not
     51  // exact.
     52  bool mayHaveFoldedStub_ : 1;
     53 
     54  // Number of optimized stubs currently attached to this IC.
     55  uint8_t numOptimizedStubs_;
     56 
     57  // Number of times we failed to attach a stub.
     58  uint8_t numFailures_;
     59 
     60  static const size_t MaxOptimizedStubs = 6;
     61 
     62  void setMode(Mode mode) {
     63    mode_ = uint32_t(mode);
     64    MOZ_ASSERT(Mode(mode_) == mode, "mode must fit in bitfield");
     65  }
     66 
     67  void transition(Mode mode) {
     68    MOZ_ASSERT(mode > this->mode());
     69    setMode(mode);
     70    numFailures_ = 0;
     71  }
     72 
     73  MOZ_ALWAYS_INLINE size_t maxFailures() const {
     74    // Allow more failures if we attached stubs.
     75    static_assert(MaxOptimizedStubs == 6,
     76                  "numFailures_/maxFailures should fit in uint8_t");
     77    size_t res = 5 + size_t(40) * numOptimizedStubs_;
     78    MOZ_ASSERT(res <= UINT8_MAX, "numFailures_ should not overflow");
     79    return res;
     80  }
     81 
     82 public:
     83  ICState() { reset(); }
     84 
     85  Mode mode() const { return Mode(mode_); }
     86  size_t numOptimizedStubs() const { return numOptimizedStubs_; }
     87  bool hasFailures() const { return (numFailures_ != 0); }
     88  bool newStubIsFirstStub() const {
     89    return (mode() == Mode::Specialized && numOptimizedStubs() == 0);
     90  }
     91 
     92  MOZ_ALWAYS_INLINE bool canAttachStub() const {
     93    // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
     94    // because old-style baseline ICs may attach more stubs than
     95    // MaxOptimizedStubs allows.
     96    if (mode() == Mode::Generic || JitOptions.disableCacheIR) {
     97      return false;
     98    }
     99    return true;
    100  }
    101 
    102  [[nodiscard]] MOZ_ALWAYS_INLINE bool shouldTransition() {
    103    // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
    104    // because old-style baseline ICs may attach more stubs than
    105    // MaxOptimizedStubs allows.
    106    if (mode() == Mode::Generic) {
    107      return false;
    108    }
    109    if (numOptimizedStubs_ < MaxOptimizedStubs &&
    110        numFailures_ < maxFailures()) {
    111      return false;
    112    }
    113    return true;
    114  }
    115 
    116  // If this returns true, we transitioned to a new mode and the caller
    117  // should discard all stubs.
    118  [[nodiscard]] MOZ_ALWAYS_INLINE bool maybeTransition() {
    119    if (!shouldTransition()) {
    120      return false;
    121    }
    122    forceTransition();
    123    return true;
    124  }
    125 
    126  MOZ_ALWAYS_INLINE void forceTransition() {
    127    if (numFailures_ >= maxFailures() || mode() == Mode::Megamorphic) {
    128      transition(Mode::Generic);
    129    } else {
    130      MOZ_ASSERT(mode() == Mode::Specialized);
    131      transition(Mode::Megamorphic);
    132    }
    133  }
    134 
    135  void reset() {
    136    setMode(Mode::Specialized);
    137 #ifdef DEBUG
    138    if (JitOptions.forceMegamorphicICs) {
    139      setMode(Mode::Megamorphic);
    140    }
    141 #endif
    142    trialInliningState_ = uint32_t(TrialInliningState::Initial);
    143    usedByTranspiler_ = false;
    144    mayHaveFoldedStub_ = false;
    145    numOptimizedStubs_ = 0;
    146    numFailures_ = 0;
    147  }
    148  void trackAttached() {
    149    // We'd like to assert numOptimizedStubs_ < MaxOptimizedStubs, but
    150    // since this code is also used for non-CacheIR Baseline stubs, assert
    151    // < 16 for now. Note that we do have the stronger assert in other
    152    // methods, because they are only used by CacheIR ICs.
    153    MOZ_ASSERT(numOptimizedStubs_ < 16);
    154    numOptimizedStubs_++;
    155    // As a heuristic, reduce the failure count after each successful attach
    156    // to delay hitting Generic mode. Reset to 1 instead of 0 so that
    157    // code which inspects state can distinguish no-failures from rare-failures.
    158    numFailures_ = std::min(numFailures_, static_cast<uint8_t>(1));
    159  }
    160  void trackNotAttached() {
    161    // Note: we can't assert numFailures_ < maxFailures() because
    162    // maxFailures() depends on numOptimizedStubs_ and it's possible a
    163    // GC discarded stubs before we got here.
    164    numFailures_++;
    165    MOZ_ASSERT(numFailures_ > 0, "numFailures_ should not overflow");
    166  }
    167  void trackUnlinkedStub() {
    168    MOZ_ASSERT(numOptimizedStubs_ > 0);
    169    numOptimizedStubs_--;
    170  }
    171  void trackUnlinkedAllStubs() { numOptimizedStubs_ = 0; }
    172 
    173  void clearUsedByTranspiler() { usedByTranspiler_ = false; }
    174  void setUsedByTranspiler() { usedByTranspiler_ = true; }
    175  bool usedByTranspiler() const { return usedByTranspiler_; }
    176 
    177  void clearMayHaveFoldedStub() { mayHaveFoldedStub_ = false; }
    178  void setMayHaveFoldedStub() { mayHaveFoldedStub_ = true; }
    179  bool mayHaveFoldedStub() const { return mayHaveFoldedStub_; }
    180 
    181  TrialInliningState trialInliningState() const {
    182    return TrialInliningState(trialInliningState_);
    183  }
    184  void setTrialInliningState(TrialInliningState state) {
    185 #ifdef DEBUG
    186    // Moving to the Failure state is always valid. The other states should
    187    // happen in this order:
    188    //
    189    //   Initial -> Candidate --> Inlined
    190    //                        \-> MonomorphicInlined
    191    //
    192    // This ensures we perform trial inlining at most once per IC site.
    193    if (state != TrialInliningState::Failure) {
    194      switch (trialInliningState()) {
    195        case TrialInliningState::Initial:
    196          MOZ_ASSERT(state == TrialInliningState::Candidate);
    197          break;
    198        case TrialInliningState::Candidate:
    199          MOZ_ASSERT(state == TrialInliningState::Candidate ||
    200                     state == TrialInliningState::Inlined ||
    201                     state == TrialInliningState::MonomorphicInlined);
    202          break;
    203        case TrialInliningState::Inlined:
    204        case TrialInliningState::MonomorphicInlined:
    205        case TrialInliningState::Failure:
    206          MOZ_CRASH("Inlined and Failure can only change to Failure");
    207          break;
    208      }
    209    }
    210 #endif
    211 
    212    trialInliningState_ = uint32_t(state);
    213    MOZ_ASSERT(trialInliningState() == state,
    214               "TrialInliningState must fit in bitfield");
    215  }
    216 };
    217 
    218 }  // namespace jit
    219 }  // namespace js
    220 
    221 #endif /* jit_ICState_h */