Track the type of guards and react accordingly if we bail out on them. Guards that protect against out-of-memory conditions don't try to grow the tree. Instead we just resume the interpreter.
authorAndreas Gal <gal@mozilla.com>
Sun, 27 Jul 2008 16:18:53 -0700
changeset 17850 e1d7528f7744bebf9722b14848e9d1990acc3a8d
parent 17849 0d41393d023b84be41a9449f73f5ccad7d9bcfc9
child 17851 94f2e3f29c62f9485b04a80d3a38e51a78e6b7ea
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.1a1pre
Track the type of guards and react accordingly if we bail out on them. Guards that protect against out-of-memory conditions don't try to grow the tree. Instead we just resume the interpreter.
js/src/jstracer.cpp
js/src/jstracer.h
js/src/nanojit/avmplus.h
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -245,17 +245,18 @@ public:
     }
 
     LInsp ins1(LOpcode v, LInsp s0)
     {
         switch (v) {
           case LIR_fneg:
               if (isPromoteInt(s0)) {
                   LIns* result = out->ins1(LIR_neg, demote(out, s0));
-                  out->insGuard(LIR_xt, out->ins1(LIR_ov, result), recorder.snapshot());
+                  out->insGuard(LIR_xt, out->ins1(LIR_ov, result), 
+                          recorder.snapshot(OVERFLOW_EXIT));
                   return out->ins1(LIR_i2f, result);
               }
               break;
           default:;
         }
         return out->ins1(v, s0);
     }
 
@@ -291,17 +292,18 @@ public:
                value range of int32 */
             if (isPromoteInt(s0) && isPromoteInt(s1)) {
                 // demote fop to op
                 v = (LOpcode)((int)v & ~LIR64);
                 LIns* d0;
                 LIns* d1;
                 LIns* result = out->ins2(v, d0 = demote(out, s0), d1 = demote(out, s1));
                 if (!overflowSafe(d0) || !overflowSafe(d1))
-                    out->insGuard(LIR_xt, out->ins1(LIR_ov, result), recorder.snapshot());
+                    out->insGuard(LIR_xt, out->ins1(LIR_ov, result), 
+                            recorder.snapshot(OVERFLOW_EXIT));
                 return out->ins1(LIR_i2f, result);
             }
         } else if (v == LIR_or &&
                    s0->isop(LIR_lsh) && isconst(s0->oprnd2(), 16) &&
                    s1->isop(LIR_and) && isconst(s1->oprnd2(), 0xffff)) {
             LIns* msw = s0->oprnd1();
             LIns* lsw = s1->oprnd1();
             LIns* x;
@@ -969,51 +971,53 @@ js_IsLoopExit(JSContext* cx, JSScript* s
         return GET_JUMP_OFFSET(pc) < 0;
 
       default:;
     }
     return false;
 }
 
 SideExit*
-TraceRecorder::snapshot()
-{
+TraceRecorder::snapshot(ExitType exitType)
+{
+    if (exitType == BRANCH_EXIT && js_IsLoopExit(cx, cx->fp->script, cx->fp->regs->pc))
+        exitType = LOOP_EXIT;
     /* generate the entry map and stash it in the trace */
     unsigned stackSlots = nativeStackSlots(callDepth, cx->fp, *cx->fp->regs);
     trackNativeStackUse(stackSlots);
     /* reserve space for the type map */
     LIns* data = lir_buf_writer->skip((stackSlots + treeInfo->ngslots) * sizeof(uint8));
     /* setup side exit structure */
     memset(&exit, 0, sizeof(exit));
     exit.from = fragment;
     exit.calldepth = getCallDepth();
     exit.ip_adj = cx->fp->regs->pc - (jsbytecode*)fragment->root->ip;
     exit.sp_adj = ((cx->fp->regs->sp - StackBase(cx->fp)) - treeInfo->entryStackDepth)
                   * sizeof(double);
     exit.rp_adj = exit.calldepth * sizeof(void*);
-    exit.loopExit = js_IsLoopExit(cx, cx->fp->script, cx->fp->regs->pc);
+    exit.exitType = exitType;
     uint8* m = exit.typeMap = (uint8 *)data->payload();
     /* Determine the type of a store by looking at the current type of the actual value the
        interpreter is using. For numbers we have to check what kind of store we used last
        (integer or double) to figure out what the side exit show reflect in its typemap. */
     FORALL_SLOTS(cx, treeInfo->ngslots, treeInfo->gslots, callDepth,
         LIns* i = get(vp);
         *m++ = isNumber(*vp)
             ? (isPromoteInt(i) ? JSVAL_INT : JSVAL_DOUBLE)
             : JSVAL_TAG(*vp);
     );
     return &exit;
 }
 
 LIns*
-TraceRecorder::guard(bool expected, LIns* cond)
+TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType)
 {
     return lir->insGuard(expected ? LIR_xf : LIR_xt,
                          cond,
-                         snapshot());
+                         snapshot(exitType));
 }
 
 bool
 TraceRecorder::checkType(jsval& v, uint8& t)
 {
     if (t == TYPEMAP_TYPE_ANY) /* ignore unused slots */
         return true;
     if (t == JSVAL_INT) { /* initially all whole numbers cause the slot to be demoted */
@@ -1086,17 +1090,17 @@ TraceRecorder::isLoopHeader(JSContext* c
 void
 TraceRecorder::closeLoop(Fragmento* fragmento)
 {
     if (!verifyTypeStability(treeInfo->typeMap)) {
         AUDIT(unstableLoopVariable);
         debug_only(printf("Trace rejected: unstable loop variables.\n");)
         return;
     }
-    SideExit *exit = snapshot();
+    SideExit *exit = snapshot(LOOP_EXIT);
     exit->target = fragment->root;
     if (fragment == fragment->root) {
         fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), exit);
     } else {
         fragment->lastIns = lir->insGuard(LIR_x, lir->insImm(1), exit);
     }
     compile(fragmento->assm(), fragment);
     if (anchor) {
@@ -1360,21 +1364,31 @@ js_LoopEdge(JSContext* cx, jsbytecode* o
         return false;
     }
 
     GuardRecord* lr = js_ExecuteTree(cx, f);
 
     if (!lr) /* did the tree actually execute? */
         return false;
 
-    /* if the side exit terminates the loop, don't try to attach a trace here */
-    if (lr->exit->loopExit)
+    switch (lr->exit->exitType) {
+    case BRANCH_EXIT:
+        /* if its a branch, try to extend the tree */
+        return js_AttemptToExtendTree(cx, lr, f);
+    case LOOP_EXIT:
+        /* if this exits the loop, resume interpretation */
         return false;
-
-    return js_AttemptToExtendTree(cx, lr, f);
+    case OVERFLOW_EXIT:
+        /* a speculation failed, we should probably de-speculate */
+        return false;
+    default:
+        JS_ASSERT(lr->exit->exitType == nanojit::OOM_EXIT);
+        /* we ran out of heap, exit for now and re-enter once the GC ran */
+        return false;
+    }
 }
 
 void
 js_AbortRecording(JSContext* cx, jsbytecode* abortpc, const char* reason)
 {
     AUDIT(recorderAborted);
     debug_only(if (!abortpc) abortpc = cx->fp->regs->pc;
                printf("Abort recording (line %d, pc %d): %s.\n",
@@ -1835,17 +1849,18 @@ TraceRecorder::native_get(LIns* obj_ins,
 }
 
 bool
 TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
 {
     if (isNumber(v)) {
         LIns* args[] = { v_ins, cx_ins };
         v_ins = lir->insCall(F_BoxDouble, args);
-        guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)));
+        guard(false, lir->ins2(LIR_eq, v_ins, lir->insImmPtr((void*)JSVAL_ERROR_COOKIE)), 
+                OOM_EXIT);
         return true;
     }
     switch (JSVAL_TAG(v)) {
       case JSVAL_BOOLEAN:
         v_ins = lir->ins2i(LIR_or, lir->ins2i(LIR_lsh, v_ins, JSVAL_TAGBITS), JSVAL_BOOLEAN);
         return true;
     }
     return false;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -145,17 +145,18 @@ class TraceRecorder {
     bool isGlobal(jsval* p) const;
     ptrdiff_t nativeStackOffset(jsval* p) const;
     ptrdiff_t nativeGlobalOffset(jsval* p) const;
     void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8& t, 
             const char *prefix, int index, jsuword* localNames);
     void trackNativeStackUse(unsigned slots);
 
     unsigned getCallDepth() const;
-    nanojit::LIns* guard(bool expected, nanojit::LIns* cond);
+    nanojit::LIns* guard(bool expected, nanojit::LIns* cond, 
+            nanojit::ExitType exitType = nanojit::BRANCH_EXIT);
     nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
 
     nanojit::LIns* get(jsval* p);
     void set(jsval* p, nanojit::LIns* l, bool initializing = false);
 
     bool checkType(jsval& v, uint8& type);
     bool verifyTypeStability(uint8* map);
 
@@ -216,17 +217,17 @@ class TraceRecorder {
                                           nanojit::LIns*& dslots_ins, nanojit::LIns* idx_ins);
     void clearFrameSlotsFromCache();
 public:
     int backEdgeCount;
 
     TraceRecorder(JSContext* cx, nanojit::GuardRecord*, nanojit::Fragment*, uint8* typemap);
     ~TraceRecorder();
 
-    nanojit::SideExit* snapshot();
+    nanojit::SideExit* snapshot(nanojit::ExitType exitType);
     nanojit::Fragment* getFragment() const { return fragment; }
     bool isLoopHeader(JSContext* cx) const;
     void closeLoop(nanojit::Fragmento* fragmento);
     
     bool record_EnterFrame();
     bool record_LeaveFrame();
     
 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)               \
--- a/js/src/nanojit/avmplus.h
+++ b/js/src/nanojit/avmplus.h
@@ -123,26 +123,30 @@ static __inline__ unsigned long long rdt
 #endif
 
 struct JSContext;
 
 namespace nanojit
 {
 	class Fragment;
 
+	enum ExitType {
+	    BRANCH_EXIT, LOOP_EXIT, OOM_EXIT, OVERFLOW_EXIT
+	};
+	
 	struct SideExit
 	{
         intptr_t ip_adj;
 		intptr_t sp_adj;
 		intptr_t rp_adj;
 		Fragment *target;
         Fragment *from;
 		int32_t calldepth;
         uint8 *typeMap;
-        int32_t loopExit:1;
+        ExitType exitType;
 #if defined NJ_VERBOSE
 		uint32_t sid;
 #endif
 	};
 
 	class LIns;
 
 	struct GuardRecord