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 84824 35124605e1a3bb588ce15a28af9ef27b64e61a4a
parent 84823 b3113b7753c187268b4d032fa62f881fe1d4ba93
child 84825 656af9b2d4817739d1eed19c6ef3e6f1d022f95c
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs713944
milestone12.0a1
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);