MERGE LOL HG SUX0rz!
authorBrendan Eich <brendan@mozilla.org>
Wed, 13 Aug 2008 19:23:56 -0700
changeset 18162 5375ca6c1cfc9a0a71025b1bf0e544ce98129962
parent 18161 a7108427de3e55f120b5f2204c151cc792d50587 (current diff)
parent 18159 df0ca7630874415cb040fe8af267e478ab509202 (diff)
child 18163 4872699c503400d6c722589453e76d31a407d6c9
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a2pre
MERGE LOL HG SUX0rz!
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1371,26 +1371,26 @@ TraceRecorder::emitTreeCall(Fragment* in
     LIns* args[] = { lir->insImmPtr(inner), lirbuf->state }; /* reverse order */
     LIns* ret = lir->insCall(F_CallTree, args);
     /* Make a note that we now depend on that tree. */
     ti->dependentTrees.addUnique(fragment);
     /* Read back all registers, in case the called tree changed any of them. */
     SideExit* exit = lr->exit;
     import(ti, inner_sp, exit->numGlobalSlots, exit->calldepth, 
            exit->typeMap, exit->typeMap + exit->numGlobalSlots);
+    /* Store the guard pointer in case we exit on an unexpected guard */
+    lir->insStorei(lir->insImmPtr(lr), lirbuf->state, offsetof(InterpState, nestedExit));
+    /* Guard that we come out of the inner tree along the same side exit we came out when
+       we called the inner tree at recording time. */
+    guard(true, lir->ins2(LIR_eq, ret, lir->insImmPtr(lr)), NESTED_EXIT);
     /* 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));
     }
-    /* Store the guard pointer in case we exit on an unexpected guard */
-    lir->insStorei(lir->insImmPtr(lr), lirbuf->state, offsetof(InterpState, nestedExit));
-    /* Guard that we come out of the inner tree along the same side exit we came out when
-       we called the inner tree at recording time. */
-    guard(true, lir->ins2(LIR_eq, ret, lir->insImmPtr(lr)), NESTED_EXIT);
 }
 
 int
 nanojit::StackFilter::getTop(LInsp guard)
 {
     if (sp == frag->lirbuf->sp)
         return guard->exit()->sp_adj + sizeof(double);
     JS_ASSERT(sp == frag->lirbuf->rp);
@@ -1744,17 +1744,16 @@ js_ExecuteTree(JSContext* cx, Fragment* 
             js_TrashTree(cx, f);
         }
         return NULL;
     }
     
     ti->mismatchCount = 0;
 
     double* entry_sp = &stack[ti->nativeStackBase/sizeof(double)];
-    //FrameInfo* callstack = (FrameInfo*) alloca(ti->maxCallDepth * sizeof(FrameInfo));
     FrameInfo* callstack = (FrameInfo*) alloca(MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo));
     
     InterpState state;
     state.sp = (void*)entry_sp;
     state.eos = ((double*)state.sp) + MAX_NATIVE_STACK_SLOTS;
     state.rp = callstack;
     state.eor = callstack + MAX_CALL_STACK_ENTRIES;
     state.gp = global;
@@ -1766,50 +1765,71 @@ js_ExecuteTree(JSContext* cx, Fragment* 
     uint64 start = rdtsc();
 #endif
 
     JS_ASSERT(!cx->gcDontBlock);
     cx->gcDontBlock = JS_TRUE;
     GuardRecord* lr = u.func(&state, NULL);
     cx->gcDontBlock = JS_FALSE;
 
-    for (int32 i = 0; i < lr->calldepth; i++)
+    /* If we bail out on a nested exit, the compiled code returns the outermost nesting
+       guard but what we are really interested in is the innermost guard that we hit
+       instead of the guard we were expecting there. */
+    if (lr->exit->exitType == NESTED_EXIT)
+        lr = state.nestedExit;
+
+    /* sp_adj and ip_adj are relative to the tree we exit out of, not the tree we 
+       entered into (which might be different in the presence of nested trees). */
+    ti = (TreeInfo*)lr->from->root->vmprivate;
+    
+    /* While executing a tree we don't update state->rp, but we do so when we call another
+       tree. So the total call stack height is the sum of the statically calculated 
+       calldepth in the side exit (relative to the tree entry), and the difference between
+       rp and the bottom of the call stack we setup for the call. */
+    int calldepth = (((FrameInfo*)state.rp) - callstack) + lr->exit->calldepth;
+
+    for (int32 i = 0; i < calldepth; ++i)
         js_SynthesizeFrame(cx, callstack[i]);
 
+    /* Adjust sp and pc relative to the tree we exited from (not the tree we entered
+       into). These are our final values for sp and pc since js_SynthesizeFrame has
+       already taken care of all frames in between. */
     SideExit* e = lr->exit;
     JSStackFrame* fp = cx->fp;
     JS_ASSERT((e->sp_adj / sizeof(double)) + ti->entryNativeStackSlots >=
               nativeStackSlots(cx, lr->calldepth, fp));
     fp->regs->sp += (e->sp_adj / sizeof(double)) + ti->entryNativeStackSlots -
                     nativeStackSlots(cx, lr->calldepth, fp);
     fp->regs->pc = (jsbytecode*)lr->from->root->ip + e->ip_adj;
 
 #if defined(DEBUG) && defined(NANOJIT_IA32)
-    printf("leaving trace at %s:%u@%u, exitType=%d, sp=%p, ip=%p, cycles=%llu\n",
+    printf("leaving trace at %s:%u@%u, exitType=%d, sp=%d, ip=%p, cycles=%llu\n",
            fp->script->filename, js_PCToLineNumber(cx, fp->script, fp->regs->pc),
            fp->regs->pc - fp->script->code,
            lr->exit->exitType,
-           state.sp, lr->jmp,
+           fp->regs->sp - StackBase(fp), lr->jmp,
            (rdtsc() - start));
 #endif
 
-    JS_ASSERT(lr->exit->exitType != NESTED_EXIT);
-    
+    /* write back interned globals */
     FlushNativeGlobalFrame(cx, e->numGlobalSlots, ti->globalSlots.data(), e->typeMap, global);
-    FlushNativeStackFrame(cx, e->calldepth, e->typeMap + e->numGlobalSlots, stack);
     JS_ASSERT(ti->globalSlots.length() >= e->numGlobalSlots);
     JS_ASSERT(globalFrameSize == STOBJ_NSLOTS(globalObj));
     JS_ASSERT(*(uint64*)&global[globalFrameSize] == 0xdeadbeefdeadbeefLL);
-
+    
+    /* write back native stack frame */
+    FlushNativeStackFrame(cx, e->calldepth, e->typeMap + e->numGlobalSlots, stack);
+    
     AUDIT(sideExitIntoInterpreter);
 
     if (!lr) /* did the tree actually execute? */
         return NULL;
 
-    inlineCallCount += lr->exit->calldepth;
+    /* Adjust inlineCallCount by the total call depth at this point (call stack height). */
+    inlineCallCount += calldepth;
 
     return lr;
 }
 
 bool
 js_LoopEdge(JSContext* cx, jsbytecode* oldpc, uintN& inlineCallCount)
 {
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -695,11 +695,25 @@ function deepForInLoop() {
   var a = [];
   for (var i in o)
     a[j++] = i;
   return a.join("");
 }
 deepForInLoop.expected = "pqrst";
 test(deepForInLoop);
 
+function nestedExit(x) {
+    var q = 0;
+    for (var i = 0; i < 10; ++i)
+	if (x)
+	    ++q;
+}
+function nestedExitLoop() {
+    for (var j = 0; j < 10; ++j)
+	nestedExit(j < 7);
+    return "ok";
+}
+nestedExitLoop.expected = "ok";
+test(nestedExitLoop);
+
 /* Keep these at the end so that we can see the summary after the trace-debug spew. */
 print("\npassed:", passes.length && passes.join(","));
 print("\nFAILED:", fails.length && fails.join(","));