Bug 470739 - Trace more == cases. r=gal
authorJeff Walden <jwalden@mit.edu>
Fri, 26 Dec 2008 14:48:33 -0500
changeset 23223 3bd2a3f41a2c46fed7e3c03882f5ac5edb10da73
parent 23123 8eb5a5b83a938ba90488b885a5773b44a867c892
child 23224 2c980386430889f7f7f0b454c9220f18787222c3
push id4405
push userrsayre@mozilla.com
push dateThu, 01 Jan 2009 22:27:02 +0000
treeherdermozilla-central@5a1e6eac685c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs470739
milestone1.9.2a1pre
Bug 470739 - Trace more == cases. r=gal
js/src/imacros.c.out
js/src/imacros.jsasm
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -1,12 +1,61 @@
 /* GENERATED BY imacro_asm.js from imacros.jsasm -- DO NOT EDIT!!! */
 static struct {
     jsbytecode any_obj[37];
     jsbytecode obj_any[39];
+} equality_imacros = {
+    {
+/* 0*/  JSOP_DUP,
+/* 1*/  JSOP_DUP,
+/* 2*/  JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
+/* 5*/  JSOP_IFPRIMTOP, 0, 16,
+/* 8*/  JSOP_SWAP,
+/* 9*/  JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
+/*12*/  JSOP_CALL, 0, 1,
+/*15*/  JSOP_IFPRIMTOP, 0, 18,
+/*18*/  JSOP_GOTO, 0, 4,
+/*21*/  JSOP_POP,
+/*22*/  JSOP_POP,
+/*23*/  JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
+/*26*/  JSOP_CALL, 0, 0,
+/*29*/  JSOP_PRIMTOP,
+/*30*/  JSOP_GOTO, 0, 5,
+/*33*/  JSOP_SWAP,
+/*34*/  JSOP_POP,
+/*35*/  JSOP_IMACOP,
+/*36*/  JSOP_STOP,
+    },
+    {
+/* 0*/  JSOP_SWAP,
+/* 1*/  JSOP_DUP,
+/* 2*/  JSOP_DUP,
+/* 3*/  JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
+/* 6*/  JSOP_IFPRIMTOP, 0, 16,
+/* 9*/  JSOP_SWAP,
+/*10*/  JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
+/*13*/  JSOP_CALL, 0, 1,
+/*16*/  JSOP_IFPRIMTOP, 0, 18,
+/*19*/  JSOP_GOTO, 0, 4,
+/*22*/  JSOP_POP,
+/*23*/  JSOP_POP,
+/*24*/  JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
+/*27*/  JSOP_CALL, 0, 0,
+/*30*/  JSOP_PRIMTOP,
+/*31*/  JSOP_GOTO, 0, 5,
+/*34*/  JSOP_SWAP,
+/*35*/  JSOP_POP,
+/*36*/  JSOP_SWAP,
+/*37*/  JSOP_IMACOP,
+/*38*/  JSOP_STOP,
+    },
+};
+static struct {
+    jsbytecode any_obj[37];
+    jsbytecode obj_any[39];
     jsbytecode obj_obj[74];
 } binary_imacros = {
     {
 /* 0*/  JSOP_DUP,
 /* 1*/  JSOP_DUP,
 /* 2*/  JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
 /* 5*/  JSOP_IFPRIMTOP, 0, 16,
 /* 8*/  JSOP_SWAP,
--- a/js/src/imacros.jsasm
+++ b/js/src/imacros.jsasm
@@ -30,16 +30,66 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
+.igroup equality JSOP_EQ-JSOP_NE
+
+    .imacro any_obj                     # any obj
+        dup                             # any obj obj
+        dup                             # any obj obj obj
+        getprop valueOf                 # any obj obj valueOf
+        ifprimtop 1                     # any obj obj valueOf
+        swap                            # any obj valueOf obj
+        string void                     # any obj valueOf obj "void"
+        call 1                          # any obj rval
+        ifprimtop 3                     # any obj rval
+        goto 2
+1:      pop                             # any obj obj
+2:      pop                             # any obj
+        callprop toString               # any toString obj
+        call 0                          # any rval
+        primtop                         # any rval
+        goto 4                          # any rval
+3:      swap                            # any rval obj
+        pop                             # any rval
+4:      imacop                          # eqval
+        stop
+    .end
+
+    .imacro obj_any                     # obj any
+        swap                            # any obj
+        dup                             # any obj obj
+        dup                             # any obj obj obj
+        getprop valueOf                 # any obj obj valueOf
+        ifprimtop 1                     # any obj obj valueOf
+        swap                            # any obj valueOf obj
+        string void                     # any obj valueOf obj "void"
+        call 1                          # any obj lval
+        ifprimtop 3                     # any obj lval
+        goto 2
+1:      pop                             # any obj obj
+2:      pop                             # any obj
+        callprop toString               # any toString obj
+        call 0                          # any lval
+        primtop                         # any rval
+        goto 4                          # any lval
+3:      swap                            # any lval obj
+        pop                             # any lval
+4:      swap                            # lval any
+        imacop                          # eqval
+        stop
+    .end
+
+.end
+
 .igroup binary JSOP_BITOR-JSOP_MOD
 
     .imacro any_obj                     # any obj
         dup                             # any obj obj
         dup                             # any obj obj obj
         getprop valueOf                 # any obj obj valueOf
         ifprimtop 1                     # any obj obj valueOf
         swap                            # any obj valueOf obj
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1867,18 +1867,20 @@ TraceRecorder::determineSlotType(jsval* 
                               ? (fp)->imacpc = (fp)->script->code +           \
                                                SCRIPT_PC_ADJ(ip),             \
                                 (fp)->regs->pc = imacro_code[*(fp)->imacpc] + \
                                                  IMACRO_PC_ADJ(ip)            \
                               : (fp)->regs->pc = (fp)->script->code + (ip))
 
 static jsbytecode* imacro_code[JSOP_LIMIT];
 
+JS_STATIC_ASSERT(sizeof(equality_imacros) < IMACRO_PC_ADJ_LIMIT);
 JS_STATIC_ASSERT(sizeof(binary_imacros) < IMACRO_PC_ADJ_LIMIT);
 JS_STATIC_ASSERT(sizeof(add_imacros) < IMACRO_PC_ADJ_LIMIT);
+JS_STATIC_ASSERT(sizeof(unary_imacros) < IMACRO_PC_ADJ_LIMIT);
 JS_STATIC_ASSERT(sizeof(apply_imacros) < IMACRO_PC_ADJ_LIMIT);
 
 JS_REQUIRES_STACK LIns*
 TraceRecorder::snapshot(ExitType exitType)
 {
     JSStackFrame* fp = cx->fp;
     JSFrameRegs* regs = fp->regs;
     jsbytecode* pc = regs->pc;
@@ -3892,17 +3894,18 @@ TraceRecorder::monitorRecording(JSOp op)
        the "if" condition at the compile time. */
     bool flag;
     switch (op) {
       default: goto abort_recording;
 # define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \
         case x:                                                               \
           flag = record_##x();                                                \
           if (x == JSOP_ITER || x == JSOP_NEXTITER || x == JSOP_APPLY ||      \
-              JSOP_IS_BINARY(x) || JSOP_IS_UNARY(x)) {                       \
+              JSOP_IS_BINARY(x) || JSOP_IS_UNARY(x) ||                        \
+              JSOP_IS_EQUALITY(x)) {                                          \
               goto imacro;                                                    \
           }                                                                   \
         break;
 # include "jsopcode.tbl"
 # undef OPDEF
     }
 
     if (flag)
@@ -4647,116 +4650,126 @@ TraceRecorder::strictEquality(bool equal
     }
 
     set(&l, x);
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::equality(bool negate, bool tryBranchAfterCond)
 {
-    jsval& r = stackval(-1);
-    jsval& l = stackval(-2);
-    LIns* x = NULL;
-    bool cond;
-    LIns* l_ins = get(&l);
-    LIns* r_ins = get(&r);
+    jsval& rval = stackval(-1);
+    jsval& lval = stackval(-2);
+    LIns* l_ins = get(&lval);
+    LIns* r_ins = get(&rval);
+
+    return equalityHelper(lval, rval, l_ins, r_ins, negate, tryBranchAfterCond, lval);
+}
+
+JS_REQUIRES_STACK bool
+TraceRecorder::equalityHelper(jsval l, jsval r, LIns* l_ins, LIns* r_ins,
+                              bool negate, bool tryBranchAfterCond,
+                              jsval& rval)
+{
     bool fp = false;
-
-    if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
-        // Comparing equality of a string against null always produces false.
-        if ((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
-            (JSVAL_IS_NULL(r) && r_ins->isconst())) {
-            x = INS_CONST(negate);
-            cond = negate;
-        } else {
-            if (!JSVAL_IS_STRING(l) || !JSVAL_IS_STRING(r))
-                ABORT_TRACE("unsupported type for cmp vs string");
-
-            LIns* args[] = { r_ins, l_ins };
-            l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
-            r_ins = lir->insImm(0);
-            cond = js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
-        }
-    } else if (isNumber(l) || isNumber(r)) {
-        jsval tmp[2] = {l, r};
-        JSAutoTempValueRooter tvr(cx, 2, tmp);
-        
-        fp = true;
-
-        // TODO: coerce non-numbers to numbers if it's not string-on-string above
-        LIns* args[] = { l_ins, cx_ins };
-        if (l == JSVAL_NULL && l_ins->isconst()) {
-            jsdpun u;
-            u.d = js_NaN;
-            l_ins = lir->insImmq(u.u64);
+    bool cond;
+    LIns* args[] = { NULL, NULL };
+
+    /*
+     * The if chain below closely mirrors that found in 11.9.3, in general
+     * deviating from that ordering of ifs only to account for SpiderMonkey's
+     * conflation of booleans and undefined and for the possibility of
+     * confusing objects and null.  Note carefully the spec-mandated recursion
+     * in the final else clause, which terminates because Number == T recurs
+     * only if T is Object, but that must recur again to convert Object to
+     * primitive, and ToPrimitive throws if the object cannot be converted to
+     * a primitive value (which would terminate recursion).
+     */
+
+    if (getPromotedType(l) == getPromotedType(r)) {
+        if (JSVAL_TAG(l) == JSVAL_OBJECT || JSVAL_TAG(l) == JSVAL_BOOLEAN) {
+            cond = (l == r);
         } else if (JSVAL_IS_STRING(l)) {
-            l_ins = lir->insCall(&js_StringToNumber_ci, args);
-        } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
-            /*
-             * What I really want here is for undefined to be type-specialized
-             * differently from real booleans.  Failing that, I want to be able
-             * to cmov on quads.  Failing that, I want to have small forward
-             * branches.  Failing that, I want to be able to ins_choose on quads
-             * without cmov.  Failing that, eat flaming builtin!
-             */
-            l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
-        } else if (!isNumber(l)) {
-            ABORT_TRACE("unsupported LHS type for cmp vs number");
+            args[0] = r_ins, args[1] = l_ins;
+            l_ins = lir->insCall(&js_EqualStrings_ci, args);
+            r_ins = lir->insImm(1);
+            cond = js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
+        } else {
+            JS_ASSERT(isNumber(l) && isNumber(r));
+            cond = (asNumber(l) == asNumber(r));
+            fp = true;
         }
-        jsdouble lnum = js_ValueToNumber(cx, &tmp[0]);
-
-        args[0] = r_ins;
-        args[1] = cx_ins;
-        if (r == JSVAL_NULL && r_ins->isconst()) {
-            jsdpun u;
-            u.d = js_NaN;
-            r_ins = lir->insImmq(u.u64);
-        } else if (JSVAL_IS_STRING(r)) {
-            r_ins = lir->insCall(&js_StringToNumber_ci, args);
-        } else if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
-            // See above for the sob story.
-            r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
-        } else if (!isNumber(r)) {
-            ABORT_TRACE("unsupported RHS type for cmp vs number");
+    } else if (JSVAL_IS_NULL(l) && JSVAL_TAG(r) == JSVAL_BOOLEAN) {
+        l_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        cond = (r == JSVAL_VOID);
+    } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN && JSVAL_IS_NULL(r)) {
+        r_ins = lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_VOID));
+        cond = (l == JSVAL_VOID);
+    } else if (isNumber(l) && JSVAL_IS_STRING(r)) {
+        args[0] = r_ins, args[1] = cx_ins;
+        r_ins = lir->insCall(&js_StringToNumber_ci, args);
+        cond = (asNumber(l) == js_StringToNumber(cx, JSVAL_TO_STRING(r)));
+        fp = true;
+    } else if (JSVAL_IS_STRING(l) && isNumber(r)) {
+        args[0] = l_ins, args[1] = cx_ins;
+        l_ins = lir->insCall(&js_StringToNumber_ci, args);
+        cond = (js_StringToNumber(cx, JSVAL_TO_STRING(l)) == asNumber(r));
+        fp = true;
+    } else {
+        if (JSVAL_TAG(l) == JSVAL_BOOLEAN) {
+            args[0] = l_ins, args[1] = cx_ins;
+            l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
+            l = (l == JSVAL_VOID)
+                ? DOUBLE_TO_JSVAL(&cx->runtime->jsNaN)
+                : INT_TO_JSVAL(l == JSVAL_TRUE);
+            return equalityHelper(l, r, l_ins, r_ins, negate,
+                                  tryBranchAfterCond, rval);
         }
-        jsdouble rnum = js_ValueToNumber(cx, &tmp[1]);
-        cond = (lnum == rnum);
-    } else if ((JSVAL_TAG(l) == JSVAL_BOOLEAN && JSVAL_TAG(r) == JSVAL_BOOLEAN) ||
-               (JSVAL_TAG(l) == JSVAL_OBJECT && JSVAL_TAG(r) == JSVAL_OBJECT)) {
-        cond = (l == r); 
-    } else {
-        ABORT_TRACE("unsupported operand types for cmp");
-    }
-
-    /* If we didn't generate a constant result yet, then emit the comparison now. */
-    if (!x) {
-        /* If the result is not a number or it's not a quad, we must use an integer compare. */
-        LOpcode op = fp ? LIR_feq : LIR_eq;
-        x = lir->ins2(op, l_ins, r_ins);
-        if (negate) {
-            x = lir->ins_eq0(x);
-            cond = !cond;
+        if (JSVAL_TAG(r) == JSVAL_BOOLEAN) {
+            args[0] = r_ins, args[1] = cx_ins;
+            r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
+            r = (r == JSVAL_VOID)
+                ? DOUBLE_TO_JSVAL(&cx->runtime->jsNaN)
+                : INT_TO_JSVAL(r == JSVAL_TRUE);
+            return equalityHelper(l, r, l_ins, r_ins, negate,
+                                  tryBranchAfterCond, rval);
         }
+        
+        if ((JSVAL_IS_STRING(l) || isNumber(l)) && !JSVAL_IS_PRIMITIVE(r))
+            return call_imacro(equality_imacros.any_obj);
+        if (!JSVAL_IS_PRIMITIVE(l) && (JSVAL_IS_STRING(r) || isNumber(r)))
+            return call_imacro(equality_imacros.obj_any);
+
+        l_ins = lir->insImm(0);
+        r_ins = lir->insImm(1);
+        cond = false;
+    }
+
+    /* If the operands aren't numbers, compare them as integers. */
+    LOpcode op = fp ? LIR_feq : LIR_eq;
+    LIns* x = lir->ins2(op, l_ins, r_ins);
+    if (negate) {
+        x = lir->ins_eq0(x);
+        cond = !cond;
     }
 
     /*
      * Don't guard if the same path is always taken.  If it isn't, we have to
      * fuse comparisons and the following branch, because the interpreter does
      * that.
      */
     if (tryBranchAfterCond && !x->isconst())
         fuseIf(cx->fp->regs->pc + 1, cond, x);
 
     /*
      * We update the stack after the guard. This is safe since the guard bails
      * out at the comparison and the interpreter will therefore re-execute the
      * comparison. This way the value of the condition doesn't have to be 
      * calculated and saved on the stack in most cases.
      */
-    set(&l, x);
+    set(&rval, x);
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond)
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
@@ -8641,16 +8654,19 @@ InitIMacroCode()
     imacro_code[JSOP_ADD] = (jsbytecode*)&add_imacros - 1;
 
     imacro_code[JSOP_ITER] = (jsbytecode*)&iter_imacros - 1;
     imacro_code[JSOP_NEXTITER] = nextiter_imacro - 1;
     imacro_code[JSOP_APPLY] = (jsbytecode*)&apply_imacros - 1;
 
     imacro_code[JSOP_NEG] = (jsbytecode*)&unary_imacros - 1;
     imacro_code[JSOP_POS] = (jsbytecode*)&unary_imacros - 1;
+
+    imacro_code[JSOP_EQ] = (jsbytecode*)&equality_imacros - 1;
+    imacro_code[JSOP_NE] = (jsbytecode*)&equality_imacros - 1;
 }
 
 #define UNUSED(n)                                                             \
     JS_REQUIRES_STACK bool                                                    \
     TraceRecorder::record_JSOP_UNUSED##n() {                                  \
         JS_NOT_REACHED("JSOP_UNUSED" # n);                                    \
         return false;                                                         \
     }
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -361,16 +361,20 @@ class TraceRecorder : public avmplus::GC
     JS_REQUIRES_STACK bool inc(jsval& v, jsint incr, bool pre = true);
     JS_REQUIRES_STACK bool inc(jsval& v, nanojit::LIns*& v_ins, jsint incr, bool pre = true);
     JS_REQUIRES_STACK bool incProp(jsint incr, bool pre = true);
     JS_REQUIRES_STACK bool incElem(jsint incr, bool pre = true);
     JS_REQUIRES_STACK bool incName(jsint incr, bool pre = true);
 
     JS_REQUIRES_STACK void strictEquality(bool equal, bool cmpCase);
     JS_REQUIRES_STACK bool equality(bool negate, bool tryBranchAfterCond);
+    JS_REQUIRES_STACK bool equalityHelper(jsval l, jsval r,
+                                          nanojit::LIns* l_ins, nanojit::LIns* r_ins,
+                                          bool negate, bool tryBranchAfterCond,
+                                          jsval& rval);
     JS_REQUIRES_STACK bool relational(nanojit::LOpcode op, bool tryBranchAfterCond);
 
     JS_REQUIRES_STACK bool unary(nanojit::LOpcode op);
     JS_REQUIRES_STACK bool binary(nanojit::LOpcode op);
 
     bool ibinary(nanojit::LOpcode op);
     bool iunary(nanojit::LOpcode op);
     bool bbinary(nanojit::LOpcode op);
@@ -482,16 +486,17 @@ public:
 #undef OPDEF
 };
 #define TRACING_ENABLED(cx)       JS_HAS_OPTION(cx, JSOPTION_JIT)
 #define TRACE_RECORDER(cx)        (JS_TRACE_MONITOR(cx).recorder)
 #define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
 
 #define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
 #define JSOP_IS_UNARY(op) ((uintN)((op) - JSOP_NEG) <= (uintN)(JSOP_POS - JSOP_NEG))
+#define JSOP_IS_EQUALITY(op) ((uintN)((op) - JSOP_EQ) <= (uintN)(JSOP_NE - JSOP_EQ))
 
 #define TRACE_ARGS_(x,args)                                                   \
     JS_BEGIN_MACRO                                                            \
         TraceRecorder* tr_ = TRACE_RECORDER(cx);                              \
         if (tr_ && !tr_->record_##x args)                                     \
             js_AbortRecording(cx, #x);                                        \
     JS_END_MACRO