Bug 713944 -Convert PropertyCacheEntry::vindex into two separate, private fields, and add some accessors for them. r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Thu, 29 Dec 2011 05:43:57 -0600
changeset 83599 35124605e1a3bb588ce15a28af9ef27b64e61a4a
parent 83598 b3113b7753c187268b4d032fa62f881fe1d4ba93
child 83600 656af9b2d4817739d1eed19c6ef3e6f1d022f95c
push id21773
push userphilringnalda@gmail.com
push dateSun, 01 Jan 2012 02:50:39 +0000
treeherdermozilla-central@da6c33eb4b16 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs713944
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 713944 -Convert PropertyCacheEntry::vindex into two separate, private fields, and add some accessors for them. r=jorendorff
js/src/jsinterp.cpp
js/src/jspropertycache.cpp
js/src/jspropertycache.h
js/src/jspropertycacheinlines.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3157,26 +3157,26 @@ BEGIN_CASE(JSOP_SETMETHOD)
              * know that the entry applies to regs.pc and that obj's shape
              * matches.
              *
              * The entry predicts a set either an existing "own" property, or
              * on a prototype property that has a setter.
              */
             const Shape *shape = entry->prop;
             JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
-            JS_ASSERT_IF(shape->hasSlot(), !entry->vindex);
-
-            if (entry->vindex == 0 ||
+            JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
+
+            if (entry->isOwnPropertyHit() ||
                 ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
 #ifdef DEBUG
-                if (entry->directHit()) {
+                if (entry->isOwnPropertyHit()) {
                     JS_ASSERT(obj->nativeContains(cx, *shape));
                 } else {
                     JS_ASSERT(obj2->nativeContains(cx, *shape));
-                    JS_ASSERT(entry->vindex == 1);
+                    JS_ASSERT(entry->isPrototypePropertyHit());
                     JS_ASSERT(entry->kshape != entry->pshape);
                     JS_ASSERT(!shape->hasSlot());
                 }
 #endif
 
                 PCMETER(cache->pchits++);
                 PCMETER(cache->setpchits++);
                 NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
@@ -4461,17 +4461,18 @@ BEGIN_CASE(JSOP_INITMETHOD)
      * added by a NEWOBJECT or a previous INITPROP. If the cached shape has a
      * non-default setter, it must be __proto__, so don't handle this.
      */
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom;
     if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, obj, &entry, &obj2, &atom) &&
         entry->prop->hasDefaultSetter() &&
-        entry->vindex == 0) {
+        entry->isOwnPropertyHit())
+    {
         JS_ASSERT(obj == obj2);
         /* Fast path. Property cache hit. */
         obj->nativeSetSlotWithType(cx, entry->prop, rval);
     } else {
         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
         LOAD_ATOM(0, atom);
 
         /* Get the immediate property name into id. */
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -75,17 +75,17 @@ PropertyCache::fill(JSContext *cx, JSObj
      * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
      * before any running script might consult a parent-linked scope chain. If
      * this requirement is not satisfied, the fill in progress will never hit,
      * but scope shape tests ensure nothing malfunctions.
      */
     JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
 
     JSObject *tmp = obj;
-    for (uintN i = 0; i != scopeIndex; i++)
+    for (uintN i = 0; i < scopeIndex; i++)
         tmp = tmp->internalScopeChain();
 
     uintN protoIndex = 0;
     while (tmp != pobj) {
 
         /*
          * Don't cache entries across prototype lookups which can mutate in
          * arbitrary ways without a shape change.
@@ -104,17 +104,18 @@ PropertyCache::fill(JSContext *cx, JSObj
          */
         if (!tmp || !tmp->isNative()) {
             PCMETER(noprotos++);
             return JS_NO_PROP_CACHE_FILL;
         }
         ++protoIndex;
     }
 
-    if (scopeIndex > PCINDEX_SCOPEMASK || protoIndex > PCINDEX_PROTOMASK) {
+    typedef PropertyCacheEntry Entry;
+    if (scopeIndex > Entry::MaxScopeIndex || protoIndex > Entry::MaxProtoIndex) {
         PCMETER(longchains++);
         return JS_NO_PROP_CACHE_FILL;
     }
 
     /*
      * Optimize the cached vword based on our parameters and the current pc's
      * opcode format flags.
      */
@@ -191,17 +192,16 @@ PropertyCache::fullTest(JSContext *cx, j
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(uint32_t(pc - script->code) < script->length);
 
     JSOp op = JSOp(*pc);
     const JSCodeSpec &cs = js_CodeSpec[op];
 
     obj = *objp;
-    uint32_t vindex = entry->vindex;
 
     if (entry->kpc != pc) {
         PCMETER(kpcmisses++);
 
         JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
 #ifdef DEBUG_notme
         JSAutoByteString printable;
         fprintf(stderr,
@@ -229,33 +229,35 @@ PropertyCache::fullTest(JSContext *cx, j
 
     /*
      * PropertyCache::test handles only the direct and immediate-prototype hit
      * cases. All others go here.
      */
     pobj = obj;
 
     if (JOF_MODE(cs.format) == JOF_NAME) {
-        while (vindex & (PCINDEX_SCOPEMASK << PCINDEX_PROTOBITS)) {
+        uint8_t scopeIndex = entry->scopeIndex;
+        while (scopeIndex > 0) {
             tmp = pobj->scopeChain();
             if (!tmp || !tmp->isNative())
                 break;
             pobj = tmp;
-            vindex -= PCINDEX_PROTOSIZE;
+            scopeIndex--;
         }
 
         *objp = pobj;
     }
 
-    while (vindex & PCINDEX_PROTOMASK) {
+    uint8_t protoIndex = entry->protoIndex;
+    while (protoIndex > 0) {
         tmp = pobj->getProto();
         if (!tmp || !tmp->isNative())
             break;
         pobj = tmp;
-        --vindex;
+        protoIndex--;
     }
 
     if (pobj->lastProperty() == entry->pshape) {
 #ifdef DEBUG
         JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
         jsid id = ATOM_TO_JSID(atom);
 
         id = js_CheckForStringIndex(id);
@@ -274,17 +276,18 @@ void
 PropertyCache::assertEmpty()
 {
     JS_ASSERT(empty);
     for (uintN i = 0; i < SIZE; i++) {
         JS_ASSERT(!table[i].kpc);
         JS_ASSERT(!table[i].kshape);
         JS_ASSERT(!table[i].pshape);
         JS_ASSERT(!table[i].prop);
-        JS_ASSERT(!table[i].vindex);
+        JS_ASSERT(!table[i].scopeIndex);
+        JS_ASSERT(!table[i].protoIndex);
     }
 }
 #endif
 
 void
 PropertyCache::purge(JSContext *cx)
 {
     if (empty) {
--- a/js/src/jspropertycache.h
+++ b/js/src/jspropertycache.h
@@ -47,49 +47,68 @@
 
 namespace js {
 
 /*
  * Property cache with structurally typed capabilities for invalidation, for
  * polymorphic callsite method/get/set speedups.  For details, see
  * <https://developer.mozilla.org/en/SpiderMonkey/Internals/Property_cache>.
  */
-
-/* Indexing for property cache entry scope and prototype chain walking. */
-enum {
-    PCINDEX_PROTOBITS = 4,
-    PCINDEX_PROTOSIZE = JS_BIT(PCINDEX_PROTOBITS),
-    PCINDEX_PROTOMASK = JS_BITMASK(PCINDEX_PROTOBITS),
-
-    PCINDEX_SCOPEBITS = 4,
-    PCINDEX_SCOPESIZE = JS_BIT(PCINDEX_SCOPEBITS),
-    PCINDEX_SCOPEMASK = JS_BITMASK(PCINDEX_SCOPEBITS)
-};
+class PropertyCache;
 
 struct PropertyCacheEntry
 {
     jsbytecode          *kpc;           /* pc of cache-testing bytecode */
     const Shape         *kshape;        /* shape of direct (key) object */
     const Shape         *pshape;        /* shape of owning object */
     const Shape         *prop;          /* shape of accessed property */
-    uint16_t            vindex;         /* scope/proto chain indexing,
-                                         * see PCINDEX above */
+
+    friend class PropertyCache;
+
+  private:
+    /* Index into scope chain; inapplicable to property lookup entries. */
+    uint8_t             scopeIndex;
+    /* Index into the prototype chain from the object for this entry. */
+    uint8_t             protoIndex;
+
+  public:
+    static const size_t MaxScopeIndex = 15;
+    static const size_t MaxProtoIndex = 15;
 
-    bool directHit() const { return vindex == 0; }
+    /*
+     * True iff the property lookup will find an own property on the object if
+     * the entry matches.
+     *
+     * This test is applicable only to property lookups, not to identifier
+     * lookups.  It is meaningless to ask this question of an entry for an
+     * identifier lookup.
+     */
+    bool isOwnPropertyHit() const { return scopeIndex == 0 && protoIndex == 0; }
+
+    /*
+     * True iff the property lookup will find the property on the prototype of
+     * the object if the entry matches.
+     *
+     * This test is applicable only to property lookups, not to identifier
+     * lookups.  It is meaningless to ask this question of an entry for an
+     * identifier lookup.
+     */
+    bool isPrototypePropertyHit() const { return scopeIndex == 0 && protoIndex == 1; }
 
     void assign(jsbytecode *kpc, const Shape *kshape, const Shape *pshape,
                 const Shape *prop, uintN scopeIndex, uintN protoIndex) {
-        JS_ASSERT(scopeIndex <= PCINDEX_SCOPEMASK);
-        JS_ASSERT(protoIndex <= PCINDEX_PROTOMASK);
+        JS_ASSERT(scopeIndex <= MaxScopeIndex);
+        JS_ASSERT(protoIndex <= MaxProtoIndex);
 
         this->kpc = kpc;
         this->kshape = kshape;
         this->pshape = pshape;
         this->prop = prop;
-        this->vindex = (scopeIndex << PCINDEX_PROTOBITS) | protoIndex;
+        this->scopeIndex = uint8_t(scopeIndex);
+        this->protoIndex = uint8_t(protoIndex);
     }
 };
 
 /*
  * Special value for functions returning PropertyCacheEntry * to distinguish
  * between failure and no no-cache-fill cases.
  */
 #define JS_NO_PROP_CACHE_FILL ((js::PropertyCacheEntry *) NULL + 1)
--- a/js/src/jspropertycacheinlines.h
+++ b/js/src/jspropertycacheinlines.h
@@ -72,24 +72,24 @@ PropertyCache::test(JSContext *cx, jsbyt
     const Shape *kshape = obj->lastProperty();
     entry = &table[hash(pc, kshape)];
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     JS_ASSERT(&obj != &pobj);
     if (entry->kpc == pc && entry->kshape == kshape) {
         JSObject *tmp;
         pobj = obj;
-        if (entry->vindex == 1 &&
+        if (entry->isPrototypePropertyHit() &&
             (tmp = pobj->getProto()) != NULL) {
             pobj = tmp;
         }
 
         if (pobj->lastProperty() == entry->pshape) {
             PCMETER(pchits++);
-            PCMETER(!entry->vindex || protopchits++);
+            PCMETER(entry->isOwnPropertyHit() || protopchits++);
             atom = NULL;
             return;
         }
     }
     atom = fullTest(cx, pc, &obj, &pobj, entry);
     if (atom)
         PCMETER(misses++);
 }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -164,26 +164,26 @@ stubs::SetName(VMFrame &f, JSAtom *origA
              * know that the entry applies to regs.pc and that obj's shape
              * matches.
              *
              * The entry predicts a set either an existing "own" property, or
              * on a prototype property that has a setter.
              */
             const Shape *shape = entry->prop;
             JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
-            JS_ASSERT_IF(shape->hasSlot(), !entry->vindex);
+            JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
 
-            if (entry->vindex == 0 ||
+            if (entry->isOwnPropertyHit() ||
                 ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
 #ifdef DEBUG
-                if (entry->directHit()) {
+                if (entry->isOwnPropertyHit()) {
                     JS_ASSERT(obj->nativeContains(cx, *shape));
                 } else {
                     JS_ASSERT(obj2->nativeContains(cx, *shape));
-                    JS_ASSERT(entry->vindex == 1);
+                    JS_ASSERT(entry->isPrototypePropertyHit());
                     JS_ASSERT(entry->kshape != entry->pshape);
                     JS_ASSERT(!shape->hasSlot());
                 }
 #endif
 
                 PCMETER(cache->pchits++);
                 PCMETER(cache->setpchits++);
                 NATIVE_SET(cx, obj, shape, entry, strict, &rval);
@@ -1641,17 +1641,18 @@ InitPropOrMethod(VMFrame &f, JSAtom *ato
      * __proto__. If shape->previous() != obj->lastProperty(), there must be a
      * repeated property name. The fast path does not handle these two cases.
      */
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom2;
     if (JS_PROPERTY_CACHE(cx).testForSet(cx, f.pc(), obj, &entry, &obj2, &atom2) &&
         entry->prop->hasDefaultSetter() &&
-        entry->vindex == 0) {
+        entry->isOwnPropertyHit())
+    {
         JS_ASSERT(obj == obj2);
         /* Fast path. Property cache hit. */
         obj->nativeSetSlotWithType(cx, entry->prop, rval);
     } else {
         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
 
         /* Get the immediate property name into id. */
         jsid id = ATOM_TO_JSID(atom);