[JAEGER] Bug 590380: restore our JSNES perf by adding a fast path for inc/dec global vars, r=dvander
authorDavid Mandelin <dmandelin@mozilla.com>
Thu, 26 Aug 2010 14:06:07 -0700
changeset 53511 169f616ec5bcf7d8ac4f59cc220fa47a6534e3d8
parent 53510 24c6d6f82b902680c4c9570eb56fcae8c58e1d68
child 53512 77a3aa93531913d5aa7dac1abe6760df69083ef6
push id15660
push userrsayre@mozilla.com
push dateSat, 11 Sep 2010 19:16:24 +0000
treeherdermozilla-central@f1bd314e64ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs590380
milestone2.0b5pre
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
[JAEGER] Bug 590380: restore our JSNES perf by adding a fast path for inc/dec global vars, r=dvander
js/src/jsemit.cpp
js/src/jsopcode.tbl
js/src/methodjit/Compiler.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -193,16 +193,20 @@ UpdateDepth(JSContext *cx, JSCodeGenerat
           case JSOP_PROPINC:
           case JSOP_PROPDEC:
             depth += 1;
             break;
           case JSOP_NAMEINC:
           case JSOP_NAMEDEC:
           case JSOP_INCNAME:
           case JSOP_DECNAME:
+          case JSOP_GNAMEINC:
+          case JSOP_GNAMEDEC:
+          case JSOP_INCGNAME:
+          case JSOP_DECGNAME:
             depth += 2;
             break;
           default:
             break;
         }
         if (depth > cg->maxStackDepth)
             cg->maxStackDepth = depth;
     }
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -394,20 +394,20 @@ OPDEF(JSOP_THROWING,      151,"throwing"
 
 /* Set and get return value pseudo-register in stack frame. */
 OPDEF(JSOP_SETRVAL,       152,"setrval",  NULL,       1,  1,  0,  2,  JOF_BYTE)
 OPDEF(JSOP_RETRVAL,       153,"retrval",  NULL,       1,  0,  0,  0,  JOF_BYTE)
 
 /* Free variable references that must either be found on the global or a ReferenceError */
 OPDEF(JSOP_GETGNAME,      154,"getgname",  NULL,       3,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_GNAME)
 OPDEF(JSOP_SETGNAME,      155,"setgname",  NULL,       3,  2,  1,  3,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME)
-OPDEF(JSOP_INCGNAME,      156,"incgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT2|JOF_GNAME)
-OPDEF(JSOP_DECGNAME,      157,"decgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT2|JOF_GNAME)
-OPDEF(JSOP_GNAMEINC,      158,"gnameinc",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2|JOF_GNAME)
-OPDEF(JSOP_GNAMEDEC,      159,"gnamedec",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2|JOF_GNAME)
+OPDEF(JSOP_INCGNAME,      156,"incgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME)
+OPDEF(JSOP_DECGNAME,      157,"decgname",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME)
+OPDEF(JSOP_GNAMEINC,      158,"gnameinc",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
+OPDEF(JSOP_GNAMEDEC,      159,"gnamedec",  NULL,       3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
 
 /* Regular expression literal requiring special "fork on exec" handling. */
 OPDEF(JSOP_REGEXP,        160,"regexp",   NULL,       3,  0,  1, 19,  JOF_REGEXP)
 
 /* XML (ECMA-357, a.k.a. "E4X") support. */
 OPDEF(JSOP_DEFXMLNS,      161,"defxmlns",   NULL,     1,  1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_ANYNAME,       162,"anyname",    NULL,     1,  0,  1, 19,  JOF_BYTE|JOF_XMLNAME)
 OPDEF(JSOP_QNAMEPART,     163,"qnamepart",  NULL,     3,  0,  1, 19,  JOF_ATOM|JOF_XMLNAME)
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -762,16 +762,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_INCNAME)
             jsop_nameinc(op, stubs::IncName, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_INCNAME)
 
           BEGIN_CASE(JSOP_INCGNAME)
             jsop_gnameinc(op, stubs::IncGlobalName, fullAtomIndex(PC));
+            break;
           END_CASE(JSOP_INCGNAME)
 
           BEGIN_CASE(JSOP_INCPROP)
             jsop_propinc(op, stubs::IncProp, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_INCPROP)
 
           BEGIN_CASE(JSOP_INCELEM)
@@ -780,16 +781,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_DECNAME)
             jsop_nameinc(op, stubs::DecName, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_DECNAME)
 
           BEGIN_CASE(JSOP_DECGNAME)
             jsop_gnameinc(op, stubs::DecGlobalName, fullAtomIndex(PC));
+            break;
           END_CASE(JSOP_DECGNAME)
 
           BEGIN_CASE(JSOP_DECPROP)
             jsop_propinc(op, stubs::DecProp, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_DECPROP)
 
           BEGIN_CASE(JSOP_DECELEM)
@@ -798,16 +800,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_NAMEINC)
             jsop_nameinc(op, stubs::NameInc, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_NAMEINC)
 
           BEGIN_CASE(JSOP_GNAMEINC)
             jsop_gnameinc(op, stubs::GlobalNameInc, fullAtomIndex(PC));
+            break;
           END_CASE(JSOP_GNAMEINC)
 
           BEGIN_CASE(JSOP_PROPINC)
             jsop_propinc(op, stubs::PropInc, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_PROPINC)
 
           BEGIN_CASE(JSOP_ELEMINC)
@@ -816,16 +819,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_NAMEDEC)
             jsop_nameinc(op, stubs::NameDec, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_NAMEDEC)
 
           BEGIN_CASE(JSOP_GNAMEDEC)
             jsop_gnameinc(op, stubs::GlobalNameDec, fullAtomIndex(PC));
+            break;
           END_CASE(JSOP_GNAMEDEC)
 
           BEGIN_CASE(JSOP_PROPDEC)
             jsop_propinc(op, stubs::PropDec, fullAtomIndex(PC));
             break;
           END_CASE(JSOP_PROPDEC)
 
           BEGIN_CASE(JSOP_ELEMDEC)
@@ -2942,20 +2946,98 @@ mjit::Compiler::jsop_this()
         stubcc.rejoin(Changes(1));
     }
 }
 
 void
 mjit::Compiler::jsop_gnameinc(JSOp op, VoidStubAtom stub, uint32 index)
 {
     JSAtom *atom = script->getAtom(index);
+#if defined JS_MONOIC
+    jsbytecode *next = &PC[JSOP_GNAMEINC_LENGTH];
+    bool pop = (JSOp(*next) == JSOP_POP) && !analysis[next].nincoming;
+    int amt = (op == JSOP_GNAMEINC || op == JSOP_INCGNAME) ? -1 : 1;
+
+    if (pop || (op == JSOP_INCGNAME || op == JSOP_DECGNAME)) {
+        /* These cases are easy, the original value is not observed. */
+
+        jsop_getgname(index);
+        // V
+
+        frame.push(Int32Value(amt));
+        // V 1
+
+        /* Use sub since it calls ValueToNumber instead of string concat. */
+        jsop_binary(JSOP_SUB, stubs::Sub);
+        // N+1
+
+        jsop_bindgname();
+        // V+1 OBJ
+
+        frame.dup2();
+        // V+1 OBJ V+1 OBJ
+
+        frame.shift(-3);
+        // OBJ OBJ V+1
+
+        frame.shift(-1);
+        // OBJ V+1
+
+        jsop_setgname(index);
+        // V+1
+
+        if (pop)
+            frame.pop();
+    } else {
+        /* The pre-value is observed, making this more tricky. */
+
+        jsop_getgname(index);
+        // V
+
+        jsop_pos();
+        // N
+
+        frame.dup();
+        // N N
+
+        frame.push(Int32Value(-amt));
+        // N N 1
+
+        jsop_binary(JSOP_ADD, stubs::Add);
+        // N N+1
+
+        jsop_bindgname();
+        // N N+1 OBJ
+
+        frame.dup2();
+        // N N+1 OBJ N+1 OBJ
+
+        frame.shift(-3);
+        // N OBJ OBJ N+1
+
+        frame.shift(-1);
+        // N OBJ N+1
+
+        jsop_setgname(index);
+        // N N+1
+
+        frame.pop();
+        // N
+    }
+
+    if (pop)
+        PC += JSOP_POP_LENGTH;
+#else
     prepareStubCall(Uses(0));
     masm.move(ImmPtr(atom), Registers::ArgReg1);
     stubCall(stub);
     frame.pushSynced();
+#endif
+
+    PC += JSOP_GNAMEINC_LENGTH;
 }
 
 void
 mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
 {
     JSAtom *atom = script->getAtom(index);
 #if defined JS_POLYIC
     jsbytecode *next = &PC[JSOP_NAMEINC_LENGTH];
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -134,30 +134,36 @@ PatchSetFallback(VMFrame &f, ic::MICInfo
 void JS_FASTCALL
 ic::SetGlobalName(VMFrame &f, uint32 index)
 {
     JSObject *obj = f.fp()->getScopeChain()->getGlobal();
     ic::MICInfo &mic = f.fp()->getScript()->mics[index];
     JSAtom *atom = f.fp()->getScript()->getAtom(GET_INDEX(f.regs.pc));
     jsid id = ATOM_TO_JSID(atom);
 
+    // The property cache doesn't like inc ops, so we use a simpler
+    // stub for that case.
+    VoidStubAtom stub = js_CodeSpec[*f.regs.pc].format & (JOF_INC | JOF_DEC)
+                      ? stubs::SetGlobalNameDumb
+                      : stubs::SetGlobalName;
+
     JS_ASSERT(mic.kind == ic::MICInfo::SET);
 
     JS_LOCK_OBJ(f.cx, obj);
     JSScope *scope = obj->scope();
     JSScopeProperty *sprop = scope->lookup(id);
     if (!sprop ||
         !sprop->hasDefaultGetterOrIsMethod() ||
         !sprop->writable() ||
         !SPROP_HAS_VALID_SLOT(sprop, scope))
     {
         JS_UNLOCK_SCOPE(f.cx, scope);
         if (sprop)
             PatchSetFallback(f, mic);
-        stubs::SetGlobalName(f, atom);
+        stub(f, atom);
         return;
     }
     uint32 shape = obj->shape();
     uint32 slot = sprop->slot;
     JS_UNLOCK_SCOPE(f.cx, scope);
 
     mic.u.name.touched = true;
 
@@ -183,18 +189,18 @@ ic::SetGlobalName(VMFrame &f, uint32 ind
 #elif defined JS_CPU_ARM
     // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch'
     // knows how to dereference it to find the integer value.
     stores.repatch(mic.load.dataLabel32AtOffset(0), slot);
 #elif defined JS_PUNBOX64
     stores.repatch(mic.load.dataLabel32AtOffset(mic.patchValueOffset), slot);
 #endif
 
-    /* Do load anyway... this time. */
-    stubs::SetGlobalName(f, atom);
+    // Actually implement the op the slow way.
+    stub(f, atom);
 }
 
 #ifdef JS_CPU_X86
 
 ic::NativeCallCompiler::NativeCallCompiler()
     : jumps(SystemAllocPolicy())
 {}
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -303,16 +303,33 @@ stubs::SetName(VMFrame &f, JSAtom *origA
                 THROW();
         }
     } while (0);
 
     f.regs.sp[-2] = f.regs.sp[-1];
 }
 
 void JS_FASTCALL
+stubs::SetGlobalNameDumb(VMFrame &f, JSAtom *atom)
+{
+    JSContext *cx = f.cx;
+
+    Value rval = f.regs.sp[-1];
+    Value &lref = f.regs.sp[-2];
+    JSObject *obj = ValueToObject(cx, &lref);
+    if (!obj)
+        THROW();
+    jsid id = ATOM_TO_JSID(atom);
+    if (!obj->setProperty(cx, id, &rval))
+        THROW();
+
+    f.regs.sp[-2] = f.regs.sp[-1];
+}
+
+void JS_FASTCALL
 stubs::SetGlobalName(VMFrame &f, JSAtom *atom)
 {
     SetName(f, atom);
 }
 
 static JSObject *
 NameOp(VMFrame &f, JSObject *obj, bool callname = false)
 {
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -77,16 +77,17 @@ void * JS_FASTCALL InvokeTracer(VMFrame 
 #else
 void * JS_FASTCALL InvokeTracer(VMFrame &f);
 #endif
 
 void JS_FASTCALL BindName(VMFrame &f);
 JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);
 void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom);
+void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL Name(VMFrame &f);
 void JS_FASTCALL GetProp(VMFrame &f);
 void JS_FASTCALL GetElem(VMFrame &f);
 void JS_FASTCALL CallElem(VMFrame &f);
 void JS_FASTCALL SetElem(VMFrame &f);
 void JS_FASTCALL Length(VMFrame &f);
 void JS_FASTCALL CallName(VMFrame &f);
 void JS_FASTCALL GetUpvar(VMFrame &f, uint32 index);