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 // ICState stores information about a Baseline or Ion IC.
16 class ICState {
17  public:
18   // When we attach the maximum number of stubs, we discard all stubs and
19   // transition the IC to Megamorphic to attach stubs that are more generic
20   // (handle more cases). If we again attach the maximum number of stubs, we
21   // transition to Generic and (depending on the IC) will either attach a
22   // single stub that handles everything or stop attaching new stubs.
23   //
24   // We also transition to Generic when we repeatedly fail to attach a stub,
25   // to avoid wasting time trying.
26   enum class Mode : uint8_t { Specialized = 0, Megamorphic, Generic };
27 
28  private:
29   Mode mode_;
30 
31   // Number of optimized stubs currently attached to this IC.
32   uint8_t numOptimizedStubs_;
33 
34   // Number of times we failed to attach a stub.
35   uint8_t numFailures_;
36 
37   static const size_t MaxOptimizedStubs = 6;
38 
transition(Mode mode)39   void transition(Mode mode) {
40     MOZ_ASSERT(mode > mode_);
41     mode_ = mode;
42     numFailures_ = 0;
43   }
44 
maxFailures()45   MOZ_ALWAYS_INLINE size_t maxFailures() const {
46     // Allow more failures if we attached stubs.
47     static_assert(MaxOptimizedStubs == 6,
48                   "numFailures_/maxFailures should fit in uint8_t");
49     size_t res = 5 + size_t(40) * numOptimizedStubs_;
50     MOZ_ASSERT(res <= UINT8_MAX, "numFailures_ should not overflow");
51     return res;
52   }
53 
54  public:
ICState()55   ICState() { reset(); }
56 
mode()57   Mode mode() const { return mode_; }
numOptimizedStubs()58   size_t numOptimizedStubs() const { return numOptimizedStubs_; }
hasFailures()59   bool hasFailures() const { return (numFailures_ != 0); }
60 
canAttachStub()61   MOZ_ALWAYS_INLINE bool canAttachStub() const {
62     // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
63     // because old-style baseline ICs may attach more stubs than
64     // MaxOptimizedStubs allows.
65     if (mode_ == Mode::Generic || JitOptions.disableCacheIR) {
66       return false;
67     }
68     return true;
69   }
70 
71   // If this returns true, we transitioned to a new mode and the caller
72   // should discard all stubs.
maybeTransition()73   MOZ_MUST_USE MOZ_ALWAYS_INLINE bool maybeTransition() {
74     // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
75     // because old-style baseline ICs may attach more stubs than
76     // MaxOptimizedStubs allows.
77     if (mode_ == Mode::Generic) {
78       return false;
79     }
80     if (numOptimizedStubs_ < MaxOptimizedStubs &&
81         numFailures_ < maxFailures()) {
82       return false;
83     }
84     if (numFailures_ == maxFailures() || mode_ == Mode::Megamorphic) {
85       transition(Mode::Generic);
86       return true;
87     }
88     MOZ_ASSERT(mode_ == Mode::Specialized);
89     transition(Mode::Megamorphic);
90     return true;
91   }
reset()92   void reset() {
93     mode_ = Mode::Specialized;
94     numOptimizedStubs_ = 0;
95     numFailures_ = 0;
96   }
trackAttached()97   void trackAttached() {
98     // We'd like to assert numOptimizedStubs_ < MaxOptimizedStubs, but
99     // since this code is also used for non-CacheIR Baseline stubs, assert
100     // < 16 for now. Note that we do have the stronger assert in other
101     // methods, because they are only used by CacheIR ICs.
102     MOZ_ASSERT(numOptimizedStubs_ < 16);
103     numOptimizedStubs_++;
104     // As a heuristic, reduce the failure count after each successful attach
105     // to delay hitting Generic mode. Reset to 1 instead of 0 so that
106     // BaselineInspector can distinguish no-failures from rare-failures.
107     numFailures_ = std::min(numFailures_, static_cast<uint8_t>(1));
108   }
trackNotAttached()109   void trackNotAttached() {
110     // Note: we can't assert numFailures_ < maxFailures() because
111     // maxFailures() depends on numOptimizedStubs_ and it's possible a
112     // GC discarded stubs before we got here.
113     numFailures_++;
114     MOZ_ASSERT(numFailures_ > 0, "numFailures_ should not overflow");
115   }
trackUnlinkedStub()116   void trackUnlinkedStub() {
117     MOZ_ASSERT(numOptimizedStubs_ > 0);
118     numOptimizedStubs_--;
119   }
trackUnlinkedAllStubs()120   void trackUnlinkedAllStubs() { numOptimizedStubs_ = 0; }
121 };
122 
123 }  // namespace jit
124 }  // namespace js
125 
126 #endif /* jit_ICState_h */
127