Bug 1494473 - Add entered counters to Baseline's CacheIR_Regular and Fallback stubs r=jandem
authorMatthew Gaudet <mgaudet@mozilla.com>
Thu, 18 Oct 2018 15:56:53 +0000
changeset 490695 2ac1bca7094c959bf382ee37779566056f8792a7
parent 490694 d1e4e5029ffb1e38b6328b8c9d422edfb4603107
child 490696 4bdddb2c5100121222fef51a89da476fed899b42
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjandem
bugs1494473
milestone64.0a1
Bug 1494473 - Add entered counters to Baseline's CacheIR_Regular and Fallback stubs r=jandem By adding a counter increment to the entry of an IC stub, it is possible to analyze the IC chain for patterns that indicate how the ICs are performing, which can influence the baseline inspector, and allow Ion to weight the choices it makes on IC data. Differential Revision: https://phabricator.services.mozilla.com/D8859
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -33,34 +33,36 @@ CacheRegisterAllocator::addressOf(MacroA
 }
 
 // BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
 class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
 {
 
     bool inStubFrame_;
     bool makesGCCalls_;
+    BaselineCacheIRStubKind kind_;
 
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
     MOZ_MUST_USE bool tailCallVM(MacroAssembler& masm, const VMFunction& fun);
 
     MOZ_MUST_USE bool callTypeUpdateIC(Register obj, ValueOperand val, Register scratch,
                                        LiveGeneralRegisterSet saveRegs);
 
     MOZ_MUST_USE bool emitStoreSlotShared(bool isFixed);
     MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
 
   public:
     friend class AutoStubFrame;
 
     BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer,
-                            uint32_t stubDataOffset)
+                            uint32_t stubDataOffset, BaselineCacheIRStubKind stubKind)
       : CacheIRCompiler(cx, writer, stubDataOffset, Mode::Baseline, StubFieldPolicy::Address),
         inStubFrame_(false),
-        makesGCCalls_(false)
+        makesGCCalls_(false),
+        kind_(stubKind)
     {}
 
     MOZ_MUST_USE bool init(CacheKind kind);
 
     JitCode* compile();
 
     bool makesGCCalls() const { return makesGCCalls_; }
 
@@ -168,16 +170,22 @@ BaselineCacheIRCompiler::compile()
 #ifndef JS_USE_LINK_REGISTER
     // The first value contains the return addres,
     // which we pull into ICTailCallReg for tail calls.
     masm.adjustFrame(sizeof(intptr_t));
 #endif
 #ifdef JS_CODEGEN_ARM
     masm.setSecondScratchReg(BaselineSecondScratchReg);
 #endif
+    // Count stub entries: We count entries rather than successes as it much easier to
+    // ensure ICStubReg is valid at entry than at exit.
+    if (kind_ == BaselineCacheIRStubKind::Regular) {
+        Address enteredCount(ICStubReg, ICCacheIR_Regular::offsetOfEnteredCount());
+        masm.add32(Imm32(1), enteredCount);
+    }
 
     do {
         switch (reader.readOp()) {
 #define DEFINE_OP(op)                   \
           case CacheOp::op:             \
             if (!emit##op())            \
                 return nullptr;         \
             break;
@@ -2174,17 +2182,17 @@ js::jit::AttachBaselineCacheIRStub(JSCon
     // Check if we already have JitCode for this stub.
     CacheIRStubInfo* stubInfo;
     CacheIRStubKey::Lookup lookup(kind, ICStubEngine::Baseline, writer.codeStart(),
                                   writer.codeLength());
     JitCode* code = jitZone->getBaselineCacheIRStubCode(lookup, &stubInfo);
     if (!code) {
         // We have to generate stub code.
         JitContext jctx(cx, nullptr);
-        BaselineCacheIRCompiler comp(cx, writer, stubDataOffset);
+        BaselineCacheIRCompiler comp(cx, writer, stubDataOffset, stubKind);
         if (!comp.init(kind)) {
             return nullptr;
         }
 
         code = comp.compile();
         if (!code) {
             return nullptr;
         }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1602,16 +1602,17 @@ ICTypeUpdate_AnyValue::Compiler::generat
 //
 // ToBool_Fallback
 //
 
 static bool
 DoToBoolFallback(JSContext* cx, BaselineFrame* frame, ICToBool_Fallback* stub, HandleValue arg,
                  MutableHandleValue ret)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "ToBool");
 
     MOZ_ASSERT(!arg.isBoolean());
 
     TryAttachStub<ToBoolIRGenerator>("ToBool", cx, frame, stub, BaselineCacheIRStubKind::Regular, arg);
 
     bool cond = ToBoolean(arg);
     ret.setBoolean(cond);
@@ -1642,16 +1643,17 @@ ICToBool_Fallback::Compiler::generateStu
 
 //
 // ToNumber_Fallback
 //
 
 static bool
 DoToNumberFallback(JSContext* cx, ICToNumber_Fallback* stub, HandleValue arg, MutableHandleValue ret)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "ToNumber");
     ret.set(arg);
     return ToNumber(cx, ret);
 }
 
 typedef bool (*DoToNumberFallbackFn)(JSContext*, ICToNumber_Fallback*, HandleValue, MutableHandleValue);
 static const VMFunction DoToNumberFallbackInfo =
     FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, "DoToNumberFallback", TailCall,
@@ -1703,16 +1705,17 @@ StripPreliminaryObjectStubs(JSContext* c
 //
 
 static bool
 DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetElem_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(frame->script());
     StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
 
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetElem(%s)", CodeName[op]);
 
@@ -1795,16 +1798,17 @@ DoGetElemFallback(JSContext* cx, Baselin
 
 static bool
 DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_,
                        HandleValue lhs, HandleValue rhs, HandleValue receiver,
                        MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetElem_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(frame->script());
     StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
 
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetElemSuper(%s)", CodeName[op]);
 
@@ -1934,16 +1938,17 @@ SetUpdateStubData(ICCacheIR_Updated* stu
 }
 
 static bool
 DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_, Value* stack,
                   HandleValue objv, HandleValue index, HandleValue rhs)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICSetElem_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     RootedScript outerScript(cx, script);
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "SetElem(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_SETELEM ||
@@ -2232,16 +2237,17 @@ StoreToTypedArray(JSContext* cx, MacroAs
 //
 
 static bool
 DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
              HandleValue key, HandleValue objValue, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     FallbackICSpew(cx, stub, "In");
 
     if (!objValue.isObject()) {
         ReportInNotObjectError(cx, key, -2, objValue, -1);
         return false;
     }
 
@@ -2285,16 +2291,17 @@ ICIn_Fallback::Compiler::generateStubCod
 //
 
 static bool
 DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICHasOwn_Fallback* stub_,
                  HandleValue keyValue, HandleValue objValue, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     FallbackICSpew(cx, stub, "HasOwn");
 
     TryAttachStub<HasPropIRGenerator>("HasOwn", cx, frame, stub,
         BaselineCacheIRStubKind::Regular, CacheKind::HasOwn,
         keyValue, objValue);
 
     bool found;
@@ -2335,16 +2342,17 @@ ICHasOwn_Fallback::Compiler::generateStu
 //
 
 static bool
 DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_,
                   HandleObject envChain, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetName_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetName(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
 
@@ -2402,16 +2410,18 @@ ICGetName_Fallback::Compiler::generateSt
 //
 // BindName_Fallback
 //
 
 static bool
 DoBindNameFallback(JSContext* cx, BaselineFrame* frame, ICBindName_Fallback* stub,
                    HandleObject envChain, MutableHandleValue res)
 {
+    stub->incrementEnteredCount();
+
     jsbytecode* pc = stub->icEntry()->pc(frame->script());
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "BindName(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
 
     RootedPropertyName name(cx, frame->script()->getName(pc));
 
@@ -2450,16 +2460,17 @@ ICBindName_Fallback::Compiler::generateS
 //
 
 static bool
 DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame, ICGetIntrinsic_Fallback* stub_,
                        MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetIntrinsic_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetIntrinsic(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_GETINTRINSIC);
 
@@ -2537,16 +2548,17 @@ ComputeGetPropResult(JSContext* cx, Base
 }
 
 static bool
 DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_,
                   MutableHandleValue val, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetProp_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub_->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetProp(%s)", CodeName[op]);
 
     MOZ_ASSERT(op == JSOP_GETPROP ||
                op == JSOP_CALLPROP ||
@@ -2609,16 +2621,17 @@ DoGetPropFallback(JSContext* cx, Baselin
 }
 
 static bool
 DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_,
                        HandleValue receiver, MutableHandleValue val, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICGetProp_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub_->icEntry()->pc(script);
     FallbackICSpew(cx, stub, "GetPropSuper(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(JSOp(*pc) == JSOP_GETPROP_SUPER);
 
     RootedPropertyName name(cx, script->getName(pc));
@@ -2756,16 +2769,17 @@ ICGetProp_Fallback::Compiler::postGenera
 //
 
 static bool
 DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_, Value* stack,
                   HandleValue lhs, HandleValue rhs)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICSetProp_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "SetProp(%s)", CodeName[op]);
 
     MOZ_ASSERT(op == JSOP_SETPROP ||
                op == JSOP_STRICTSETPROP ||
@@ -3588,16 +3602,17 @@ TryAttachConstStringSplit(JSContext* cx,
 }
 
 static bool
 DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint32_t argc,
                Value* vp, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "Call(%s)", CodeName[op]);
 
     MOZ_ASSERT(argc == GET_ARGC(pc));
     bool constructing = (op == JSOP_NEW || op == JSOP_SUPERCALL);
@@ -3726,16 +3741,17 @@ DoCallFallback(JSContext* cx, BaselineFr
 }
 
 static bool
 DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, Value* vp,
                      MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     bool constructing = (op == JSOP_SPREADNEW || op == JSOP_SPREADSUPERCALL);
     FallbackICSpew(cx, stub, "SpreadCall(%s)", CodeName[op]);
 
     // Ensure vp array is rooted - we may GC in here.
@@ -5259,16 +5275,17 @@ ICTableSwitch::fixupJumpTable(JSScript* 
 //
 // GetIterator_Fallback
 //
 
 static bool
 DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, ICGetIterator_Fallback* stub,
                       HandleValue value, MutableHandleValue res)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "GetIterator");
 
     TryAttachStub<GetIteratorIRGenerator>("GetIterator", cx, frame, stub, BaselineCacheIRStubKind::Regular, value);
 
     JSObject* iterobj = ValueToIterator(cx, value);
     if (!iterobj) {
         return false;
     }
@@ -5303,16 +5320,17 @@ ICGetIterator_Fallback::Compiler::genera
 //
 
 static bool
 DoIteratorMoreFallback(JSContext* cx, BaselineFrame* frame, ICIteratorMore_Fallback* stub_,
                        HandleObject iterObj, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIteratorMore_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     FallbackICSpew(cx, stub, "IteratorMore");
 
     if (!IteratorMore(cx, iterObj, res)) {
         return false;
     }
 
     // Check if debug mode toggling made the stub invalid.
@@ -5436,16 +5454,17 @@ ICIteratorClose_Fallback::Compiler::gene
 //
 
 static bool
 DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub_,
                      HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICInstanceOf_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     FallbackICSpew(cx, stub, "InstanceOf");
 
     if (!rhs.isObject()) {
         ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, nullptr);
         return false;
     }
 
@@ -5504,16 +5523,17 @@ ICInstanceOf_Fallback::Compiler::generat
 //
 // TypeOf_Fallback
 //
 
 static bool
 DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICTypeOf_Fallback* stub, HandleValue val,
                  MutableHandleValue res)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "TypeOf");
 
     TryAttachStub<TypeOfIRGenerator>("TypeOf", cx, frame, stub, BaselineCacheIRStubKind::Regular, val);
 
     JSType type = js::TypeOfValue(val);
     RootedString string(cx, TypeName(type, cx->names()));
     res.setString(string);
     return true;
@@ -5535,16 +5555,17 @@ ICTypeOf_Fallback::Compiler::generateStu
 
     return tailCallVM(DoTypeOfFallbackInfo, masm);
 }
 
 static bool
 DoRetSubFallback(JSContext* cx, BaselineFrame* frame, ICRetSub_Fallback* stub,
                  HandleValue val, uint8_t** resumeAddr)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "RetSub");
 
     // |val| is the bytecode offset where we should resume.
 
     MOZ_ASSERT(val.isInt32());
     MOZ_ASSERT(val.toInt32() >= 0);
 
     JSScript* script = frame->script();
@@ -5812,16 +5833,17 @@ ICRest_Fallback::Compiler::generateStubC
 //
 
 static bool
 DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame, ICUnaryArith_Fallback* stub,
                      HandleValue val, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICUnaryArith_Fallback*> debug_stub(frame, stub);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
 
     switch (op) {
       case JSOP_BITNOT: {
@@ -5886,16 +5908,17 @@ ICUnaryArith_Fallback::Compiler::generat
 //
 
 static bool
 DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallback* stub_,
                       HandleValue lhs, HandleValue rhs, MutableHandleValue ret)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICBinaryArith_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "CacheIRBinaryArith(%s,%d,%d)", CodeName[op],
             int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()),
             int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType()));
 
@@ -6021,16 +6044,17 @@ ICBinaryArith_Fallback::Compiler::genera
 // Compare_Fallback
 //
 static bool
 DoCompareFallback(JSContext* cx, BaselineFrame* frame, ICCompare_Fallback* stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue ret)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICCompare_Fallback*> stub(frame, stub_);
+    stub->incrementEnteredCount();
 
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
 
     FallbackICSpew(cx, stub, "Compare(%s)", CodeName[op]);
 
     // Case operations in a CONDSWITCH are performing strict equality.
@@ -6131,16 +6155,17 @@ ICCompare_Fallback::Compiler::generateSt
 //
 // NewArray_Fallback
 //
 
 static bool
 DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length,
            MutableHandleValue res)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "NewArray");
 
     RootedObject obj(cx);
     if (stub->templateObject()) {
         RootedObject templateObject(cx, stub->templateObject());
         obj = NewArrayOperationWithTemplate(cx, templateObject);
         if (!obj) {
             return false;
@@ -6185,16 +6210,17 @@ ICNewArray_Fallback::Compiler::generateS
 }
 
 //
 // NewObject_Fallback
 //
 static bool
 DoNewObject(JSContext* cx, BaselineFrame* frame, ICNewObject_Fallback* stub, MutableHandleValue res)
 {
+    stub->incrementEnteredCount();
     FallbackICSpew(cx, stub, "NewObject");
 
     RootedObject obj(cx);
 
     RootedObject templateObject(cx, stub->templateObject());
     if (templateObject) {
         MOZ_ASSERT(!templateObject->group()->maybePreliminaryObjectsDontCheckGeneration());
         obj = NewObjectOperationWithTemplate(cx, templateObject);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -704,36 +704,44 @@ class ICFallbackStub : public ICStub
     friend class ICStubConstIterator;
   protected:
     // Fallback stubs need these fields to easily add new stubs to
     // the linked list of stubs for an IC.
 
     // The IC entry for this linked list of stubs.
     ICEntry* icEntry_;
 
-    // The number of stubs kept in the IC entry.
+    // The state of this IC
     ICState state_;
 
+    // Counts the number of times the stub was entered
+    //
+    // See Bug 1494473 comment 6 for a mechanism to handle overflow if overflow
+    // becomes a concern.
+    uint32_t enteredCount_;
+
     // A pointer to the location stub pointer that needs to be
     // changed to add a new "last" stub immediately before the fallback
     // stub.  This'll start out pointing to the icEntry's "firstStub_"
     // field, and as new stubs are added, it'll point to the current
     // last stub's "next_" field.
     ICStub** lastStubPtrAddr_;
 
     ICFallbackStub(Kind kind, JitCode* stubCode)
       : ICStub(kind, ICStub::Fallback, stubCode),
         icEntry_(nullptr),
         state_(),
+        enteredCount_(0),
         lastStubPtrAddr_(nullptr) {}
 
     ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode)
       : ICStub(kind, trait, stubCode),
         icEntry_(nullptr),
         state_(),
+        enteredCount_(0),
         lastStubPtrAddr_(nullptr)
     {
         MOZ_ASSERT(trait == ICStub::Fallback ||
                    trait == ICStub::MonitoredFallback);
     }
 
   public:
     inline ICEntry* icEntry() const {
@@ -804,27 +812,39 @@ class ICFallbackStub : public ICStub
         }
         return count;
     }
 
     void discardStubs(JSContext* cx);
 
     void unlinkStub(Zone* zone, ICStub* prev, ICStub* stub);
     void unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind);
+
+    // Return the number of times this stub has successfully provided a value to the
+    // caller.
+    uint32_t enteredCount() const { return enteredCount_; }
+    inline void incrementEnteredCount() { enteredCount_++; }
 };
 
 // Base class for Trait::Regular CacheIR stubs
 class ICCacheIR_Regular : public ICStub
 {
     const CacheIRStubInfo* stubInfo_;
 
+    // Counts the number of times the stub was entered
+    //
+    // See Bug 1494473 comment 6 for a mechanism to handle overflow if overflow
+    // becomes a concern.
+    uint32_t enteredCount_;
+
   public:
     ICCacheIR_Regular(JitCode* stubCode, const CacheIRStubInfo* stubInfo)
       : ICStub(ICStub::CacheIR_Regular, stubCode),
-        stubInfo_(stubInfo)
+        stubInfo_(stubInfo),
+        enteredCount_(0)
     {}
 
     static ICCacheIR_Regular* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
                                     ICCacheIR_Regular& other);
 
     void notePreliminaryObject() {
         extra_ = 1;
     }
@@ -832,16 +852,21 @@ class ICCacheIR_Regular : public ICStub
         return extra_;
     }
 
     const CacheIRStubInfo* stubInfo() const {
         return stubInfo_;
     }
 
     uint8_t* stubDataStart();
+
+    // Return the number of times this stub has successfully provided a value to the
+    // caller.
+    uint32_t enteredCount() const { return enteredCount_; }
+    static size_t offsetOfEnteredCount() { return offsetof(ICCacheIR_Regular, enteredCount_); }
 };
 
 // Monitored stubs are IC stubs that feed a single resulting value out to a
 // type monitor operation.
 class ICMonitoredStub : public ICStub
 {
   protected:
     // Pointer to the start of the type monitoring stub chain.