Consolidated inner global typemap completion logic and fixed a bug when extending nested exits (bug 510518, r=gal,sayrer).
authorDavid Anderson <danderson@mozilla.com>
Thu, 20 Aug 2009 13:06:27 -0700
changeset 31913 3a1ab9f19b66d0052cfcbb247c90508b8e25335e
parent 31912 304aa5d0cae06f411c8195f3b6b32eaf568bbbd9
child 31914 846357830a5e2732627ecfdbb1d84068ac112183
push id8746
push userrsayre@mozilla.com
push dateTue, 25 Aug 2009 16:53:43 +0000
treeherdermozilla-central@189759c41621 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal, sayrer
bugs510518
milestone1.9.3a1pre
Consolidated inner global typemap completion logic and fixed a bug when extending nested exits (bug 510518, r=gal,sayrer).
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -4178,16 +4178,36 @@ TraceRecorder::prepareTreeCall(Fragment*
                 + sp_adj /* adjust for stack in outer frame inner tree can't see */
                 + ti->nativeStackBase), /* plus the inner tree's stack base */
                 lirbuf->state, offsetof(InterpState, sp));
         lir->insStorei(lir->ins2i(LIR_piadd, lirbuf->rp, rp_adj),
                 lirbuf->state, offsetof(InterpState, rp));
     }
 }
 
+static unsigned
+BuildGlobalTypeMapFromInnerTree(Queue<JSTraceType>& typeMap, VMSideExit* inner)
+{
+#if defined DEBUG
+    unsigned initialSlots = typeMap.length();
+#endif
+    /* First, use the innermost exit's global typemap. */
+    typeMap.add(inner->globalTypeMap(), inner->numGlobalSlots);
+
+    /* Add missing global types from the innermost exit's tree. */
+    TreeInfo* innerTree = inner->root()->getTreeInfo();
+    unsigned slots = inner->numGlobalSlots;
+    if (slots < innerTree->nGlobalTypes()) {
+        typeMap.add(innerTree->globalTypeMap() + slots, innerTree->nGlobalTypes() - slots);
+        slots = innerTree->nGlobalTypes();
+    }
+    JS_ASSERT(typeMap.length() - initialSlots == slots);
+    return slots;
+}
+
 /* Record a call to an inner tree. */
 JS_REQUIRES_STACK void
 TraceRecorder::emitTreeCall(Fragment* inner, VMSideExit* exit)
 {
     TreeInfo* ti = (TreeInfo*)inner->vmprivate;
 
     /* Invoke the inner tree. */
     LIns* args[] = { INS_CONSTPTR(inner), lirbuf->state }; /* reverse order */
@@ -4205,22 +4225,17 @@ TraceRecorder::emitTreeCall(Fragment* in
         JS_ASSERT(map[i] != TT_JSVAL);
 #endif
     /*
      * Bug 502604 - It is illegal to extend from the outer typemap without
      * first extending from the inner. Make a new typemap here.
      */
     TypeMap fullMap;
     fullMap.add(exit->stackTypeMap(), exit->numStackSlots);
-    fullMap.add(exit->globalTypeMap(), exit->numGlobalSlots);
-    TreeInfo* innerTree = exit->root()->getTreeInfo();
-    if (exit->numGlobalSlots < innerTree->nGlobalTypes()) {
-        fullMap.add(innerTree->globalTypeMap() + exit->numGlobalSlots,
-                    innerTree->nGlobalTypes() - exit->numGlobalSlots);
-    }
+    BuildGlobalTypeMapFromInnerTree(fullMap, exit);
     import(ti, inner_sp_ins, exit->numStackSlots, fullMap.length() - exit->numStackSlots,
            exit->calldepth, fullMap.data());
 
     /* Restore sp and rp to their original values (we still have them in a register). */
     if (callDepth > 0) {
         lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp));
         lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp));
     }
@@ -5021,45 +5036,19 @@ AttemptToExtendTree(JSContext* cx, VMSid
              * exited from has the types for everything in the current scope
              * (and whatever it inlined). We have to merge those maps here.
              */
             VMSideExit* e1 = anchor;
             VMSideExit* e2 = exitedFrom;
             fullMap.add(e1->stackTypeMap(), e1->numStackSlotsBelowCurrentFrame);
             fullMap.add(e2->stackTypeMap(), e2->numStackSlots);
             stackSlots = fullMap.length();
-            fullMap.add(e2->globalTypeMap(), e2->numGlobalSlots);
-            if (e2->numGlobalSlots < e1->numGlobalSlots) {
-                /*
-                 * Watch out for an extremely rare case (bug 502714). The sequence of events is:
-                 *
-                 * 1) Inner tree compiles not knowing about global X (which has type A).
-                 * 2) Inner tree learns about global X and specializes it to a different type
-                 *    (type B).
-                 * 3) Outer tree records inner tree with global X as type A, exiting as B.
-                 * 4) Outer tree now has a nesting guard with typeof(X)=B.
-                 * 5) Inner tree takes its original exit that does not know about X.
-                 *
-                 * In this case, the nesting guard fails, and now it is illegal to use the nested
-                 * typemap entry for X. The correct entry is in the inner guard's TreeInfo,
-                 * analogous to the solution for bug 476653.
-                 */
-                TreeInfo* innerTree = e2->root()->getTreeInfo();
-                unsigned slots = e2->numGlobalSlots;
-                if (innerTree->nGlobalTypes() > slots) {
-                    unsigned addSlots = JS_MIN(innerTree->nGlobalTypes() - slots,
-                                               e1->numGlobalSlots - slots);
-                    fullMap.add(innerTree->globalTypeMap() + e2->numGlobalSlots, addSlots);
-                    slots += addSlots;
-                }
-                if (slots < e1->numGlobalSlots)
-                    fullMap.add(e1->globalTypeMap() + slots, e1->numGlobalSlots - slots);
-                JS_ASSERT(slots == e1->numGlobalSlots);
-            }
-            ngslots = e1->numGlobalSlots;
+            ngslots = BuildGlobalTypeMapFromInnerTree(fullMap, e2);
+            JS_ASSERT(ngslots >= e1->numGlobalSlots); // inner tree must have all globals
+            JS_ASSERT(ngslots == fullMap.length() - stackSlots);
             typeMap = fullMap.data();
         }
         JS_ASSERT(ngslots >= anchor->numGlobalSlots);
         bool rv = StartRecorder(cx, anchor, c, (TreeInfo*)f->vmprivate, stackSlots,
                                 ngslots, typeMap, exitedFrom, outer, cx->fp->argc);
 #ifdef MOZ_TRACEVIS
         if (!rv && tvso)
             tvso->r = R_FAIL_EXTEND_START;
@@ -5842,35 +5831,37 @@ LeaveTree(InterpState& state, VMSideExit
      */
     TreeInfo* outermostTree = state.outermostTree;
     uint16* gslots = outermostTree->globalSlots->data();
     unsigned ngslots = outermostTree->globalSlots->length();
     JS_ASSERT(ngslots == outermostTree->nGlobalTypes());
     JSTraceType* globalTypeMap;
 
     /* Are there enough globals? */
+    Queue<JSTraceType> typeMap(0);
     if (innermost->numGlobalSlots == ngslots) {
         /* Yes. This is the ideal fast path. */
         globalTypeMap = innermost->globalTypeMap();
     } else {
         /*
          * No. Merge the typemap of the innermost entry and exit together. This
          * should always work because it is invalid for nested trees or linked
          * trees to have incompatible types. Thus, whenever a new global type
          * is lazily added into a tree, all dependent and linked trees are
          * immediately specialized (see bug 476653).
          */
-        TreeInfo* ti = innermost->root()->getTreeInfo();
-        JS_ASSERT(ti->nGlobalTypes() == ngslots);
-        JS_ASSERT(ti->nGlobalTypes() > innermost->numGlobalSlots);
-        globalTypeMap = (JSTraceType*)alloca(ngslots * sizeof(JSTraceType));
-        memcpy(globalTypeMap, innermost->globalTypeMap(), innermost->numGlobalSlots);
-        memcpy(globalTypeMap + innermost->numGlobalSlots,
-               ti->globalTypeMap() + innermost->numGlobalSlots,
-               ti->nGlobalTypes() - innermost->numGlobalSlots);
+        JS_ASSERT(innermost->root()->getTreeInfo()->nGlobalTypes() == ngslots);
+        JS_ASSERT(innermost->root()->getTreeInfo()->nGlobalTypes() > innermost->numGlobalSlots);
+        typeMap.ensure(ngslots);
+#ifdef DEBUG
+        unsigned check_ngslots = 
+#endif
+        BuildGlobalTypeMapFromInnerTree(typeMap, innermost);
+        JS_ASSERT(check_ngslots == ngslots);
+        globalTypeMap = typeMap.data();
     }
 
     /* Write back the topmost native stack frame. */
 #ifdef DEBUG
     int slots =
 #endif
         FlushNativeStackFrame(cx, innermost->calldepth,
                               innermost->stackTypeMap(),
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -56,27 +56,28 @@
 #endif
 
 template <typename T>
 class Queue : public avmplus::GCObject {
     T* _data;
     unsigned _len;
     unsigned _max;
 
+public:
     void ensure(unsigned size) {
         if (!_max)
             _max = 16;
         while (_max < size)
             _max <<= 1;
         _data = (T*)realloc(_data, _max * sizeof(T));
 #if defined(DEBUG)
         memset(&_data[_len], 0xcd, _max - _len);
 #endif
     }
-public:
+
     Queue(unsigned max = 16) {
         this->_max = max;
         this->_len = 0;
         if (max)
             this->_data = (T*)malloc(max * sizeof(T));
         else
             this->_data = NULL;
     }