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 Failure, 22 }; 23 24 // ICState stores information about a Baseline or Ion IC. 25 class ICState { 26 public: 27 // When we attach the maximum number of stubs, we discard all stubs and 28 // transition the IC to Megamorphic to attach stubs that are more generic 29 // (handle more cases). If we again attach the maximum number of stubs, we 30 // transition to Generic and (depending on the IC) will either attach a 31 // single stub that handles everything or stop attaching new stubs. 32 // 33 // We also transition to Generic when we repeatedly fail to attach a stub, 34 // to avoid wasting time trying. 35 enum class Mode : uint8_t { Specialized = 0, Megamorphic, Generic }; 36 37 private: 38 uint8_t mode_ : 2; 39 40 // The TrialInliningState for a Baseline IC. 41 uint8_t trialInliningState_ : 2; 42 43 // Whether WarpOracle created a snapshot based on stubs attached to this 44 // Baseline IC. 45 bool usedByTranspiler_ : 1; 46 47 // Number of optimized stubs currently attached to this IC. 48 uint8_t numOptimizedStubs_; 49 50 // Number of times we failed to attach a stub. 51 uint8_t numFailures_; 52 53 static const size_t MaxOptimizedStubs = 6; 54 setMode(Mode mode)55 void setMode(Mode mode) { 56 mode_ = uint32_t(mode); 57 MOZ_ASSERT(Mode(mode_) == mode, "mode must fit in bitfield"); 58 } 59 transition(Mode mode)60 void transition(Mode mode) { 61 MOZ_ASSERT(mode > this->mode()); 62 setMode(mode); 63 numFailures_ = 0; 64 } 65 maxFailures()66 MOZ_ALWAYS_INLINE size_t maxFailures() const { 67 // Allow more failures if we attached stubs. 68 static_assert(MaxOptimizedStubs == 6, 69 "numFailures_/maxFailures should fit in uint8_t"); 70 size_t res = 5 + size_t(40) * numOptimizedStubs_; 71 MOZ_ASSERT(res <= UINT8_MAX, "numFailures_ should not overflow"); 72 return res; 73 } 74 75 public: ICState()76 ICState() { reset(); } 77 mode()78 Mode mode() const { return Mode(mode_); } numOptimizedStubs()79 size_t numOptimizedStubs() const { return numOptimizedStubs_; } hasFailures()80 bool hasFailures() const { return (numFailures_ != 0); } newStubIsFirstStub()81 bool newStubIsFirstStub() const { 82 return (mode() == Mode::Specialized && numOptimizedStubs() == 0); 83 } 84 canAttachStub()85 MOZ_ALWAYS_INLINE bool canAttachStub() const { 86 // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs 87 // because old-style baseline ICs may attach more stubs than 88 // MaxOptimizedStubs allows. 89 if (mode() == Mode::Generic || JitOptions.disableCacheIR) { 90 return false; 91 } 92 return true; 93 } 94 95 // If this returns true, we transitioned to a new mode and the caller 96 // should discard all stubs. maybeTransition()97 [[nodiscard]] MOZ_ALWAYS_INLINE bool maybeTransition() { 98 // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs 99 // because old-style baseline ICs may attach more stubs than 100 // MaxOptimizedStubs allows. 101 if (mode() == Mode::Generic) { 102 return false; 103 } 104 if (numOptimizedStubs_ < MaxOptimizedStubs && 105 numFailures_ < maxFailures()) { 106 return false; 107 } 108 if (numFailures_ == maxFailures() || mode() == Mode::Megamorphic) { 109 transition(Mode::Generic); 110 return true; 111 } 112 MOZ_ASSERT(mode() == Mode::Specialized); 113 transition(Mode::Megamorphic); 114 return true; 115 } 116 reset()117 void reset() { 118 setMode(Mode::Specialized); 119 #ifdef DEBUG 120 if (JitOptions.forceMegamorphicICs) { 121 setMode(Mode::Megamorphic); 122 } 123 #endif 124 trialInliningState_ = uint32_t(TrialInliningState::Initial); 125 usedByTranspiler_ = false; 126 numOptimizedStubs_ = 0; 127 numFailures_ = 0; 128 } trackAttached()129 void trackAttached() { 130 // We'd like to assert numOptimizedStubs_ < MaxOptimizedStubs, but 131 // since this code is also used for non-CacheIR Baseline stubs, assert 132 // < 16 for now. Note that we do have the stronger assert in other 133 // methods, because they are only used by CacheIR ICs. 134 MOZ_ASSERT(numOptimizedStubs_ < 16); 135 numOptimizedStubs_++; 136 // As a heuristic, reduce the failure count after each successful attach 137 // to delay hitting Generic mode. Reset to 1 instead of 0 so that 138 // BaselineInspector can distinguish no-failures from rare-failures. 139 numFailures_ = std::min(numFailures_, static_cast<uint8_t>(1)); 140 } trackNotAttached()141 void trackNotAttached() { 142 // Note: we can't assert numFailures_ < maxFailures() because 143 // maxFailures() depends on numOptimizedStubs_ and it's possible a 144 // GC discarded stubs before we got here. 145 numFailures_++; 146 MOZ_ASSERT(numFailures_ > 0, "numFailures_ should not overflow"); 147 } trackUnlinkedStub()148 void trackUnlinkedStub() { 149 MOZ_ASSERT(numOptimizedStubs_ > 0); 150 numOptimizedStubs_--; 151 } trackUnlinkedAllStubs()152 void trackUnlinkedAllStubs() { numOptimizedStubs_ = 0; } 153 clearUsedByTranspiler()154 void clearUsedByTranspiler() { usedByTranspiler_ = false; } setUsedByTranspiler()155 void setUsedByTranspiler() { usedByTranspiler_ = true; } usedByTranspiler()156 bool usedByTranspiler() const { return usedByTranspiler_; } 157 trialInliningState()158 TrialInliningState trialInliningState() const { 159 return TrialInliningState(trialInliningState_); 160 } setTrialInliningState(TrialInliningState state)161 void setTrialInliningState(TrialInliningState state) { 162 #ifdef DEBUG 163 // Moving to the Failure state is always valid. The other states should 164 // happen in this order: 165 // 166 // Initial -> Candidate -> Inlined 167 // 168 // This ensures we perform trial inlining at most once per IC site. 169 if (state != TrialInliningState::Failure) { 170 switch (trialInliningState()) { 171 case TrialInliningState::Initial: 172 MOZ_ASSERT(state == TrialInliningState::Candidate); 173 break; 174 case TrialInliningState::Candidate: 175 MOZ_ASSERT(state == TrialInliningState::Candidate || 176 state == TrialInliningState::Inlined); 177 break; 178 case TrialInliningState::Inlined: 179 case TrialInliningState::Failure: 180 MOZ_CRASH("Inlined and Failure can only change to Failure"); 181 break; 182 } 183 } 184 #endif 185 186 trialInliningState_ = uint32_t(state); 187 MOZ_ASSERT(trialInliningState() == state, 188 "TrialInliningState must fit in bitfield"); 189 } 190 }; 191 192 } // namespace jit 193 } // namespace js 194 195 #endif /* jit_ICState_h */ 196