Bug 469942 - TM: 20% slower to compute unary +/-. r=brendan
authorJeff Walden <jwalden@mit.edu>
Fri, 19 Dec 2008 19:30:09 -0800
changeset 23106 d0779fe10486338b57c00b35f7b5ec631bedd27d
parent 23105 c22e5761de557e3d48e18c2167f6302fc070ac99
child 23107 f13e2a2a5d66682ec1a6f695d1aeca92ea066ea2
push id4346
push userrsayre@mozilla.com
push dateFri, 26 Dec 2008 01:26:36 +0000
treeherdermozilla-central@8eb5a5b83a93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs469942
milestone1.9.2a1pre
Bug 469942 - TM: 20% slower to compute unary +/-. r=brendan
js/src/imacro_asm.js.in
js/src/imacros.c.out
js/src/imacros.jsasm
js/src/jsinterp.cpp
js/src/jsopcode.tbl
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsxdrapi.h
js/src/trace-test.js
--- a/js/src/imacro_asm.js.in
+++ b/js/src/imacro_asm.js.in
@@ -100,18 +100,20 @@ function immediate(op) {
     let info = op.info;
     if (info.flags.indexOf("JOF_ATOM") >= 0) {
         if (/^(?:void|object|function|string|number|boolean)$/.test(op.imm1))
             return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_" + op.imm1.toUpperCase() + ")";
         return "0, COMMON_ATOM_INDEX(" + op.imm1 + ")";
     }
     if (info.flags.indexOf("JOF_JUMP") >= 0)
         return ((op.target >> 8) & 0xff) + ", " + (op.target & 0xff);
-    if (info.flags.indexOf("JOF_UINT8") >= 0)
+    if (info.flags.indexOf("JOF_UINT8") >= 0 ||
+        info.flags.indexOf("JOF_INT8") >= 0) {
         return (op.imm1 & 0xff);
+    }
     if (info.flags.indexOf("JOF_UINT16") >= 0)
         return ((op.imm1 & 0xff00) >> 8) + ", " + (op.imm1 & 0xff);
     throw new Error(info.jsop + " format not yet implemented");
 }
 
 /*
  * Syntax (spaces are significant only to delimit tokens):
  *
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -1,9 +1,9 @@
-/* GENERATED BY imacro_asm.js from ../imacros.jsasm -- DO NOT EDIT!!! */
+/* GENERATED BY imacro_asm.js from imacros.jsasm -- DO NOT EDIT!!! */
 static struct {
     jsbytecode any_obj[36];
     jsbytecode obj_any[38];
     jsbytecode obj_obj[72];
 } binary_imacros = {
     {
 /* 0*/  JSOP_DUP,
 /* 1*/  JSOP_DUP,
@@ -167,16 +167,40 @@ static struct {
 /*65*/  JSOP_GOTO, 0, 5,
 /*68*/  JSOP_SWAP,
 /*69*/  JSOP_POP,
 /*70*/  JSOP_ADD,
 /*71*/  JSOP_STOP,
     },
 };
 static struct {
+    jsbytecode sign[34];
+} unary_imacros = {
+    {
+/* 0*/  JSOP_DUP,
+/* 1*/  JSOP_DUP,
+/* 2*/  JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
+/* 5*/  JSOP_IFPRIMTOP, 0, 18,
+/* 8*/  JSOP_SWAP,
+/* 9*/  JSOP_CALL, 0, 0,
+/*12*/  JSOP_IFPRIMTOP, 0, 6,
+/*15*/  JSOP_GOTO, 0, 9,
+/*18*/  JSOP_SWAP,
+/*19*/  JSOP_POP,
+/*20*/  JSOP_GOTO, 0, 12,
+/*23*/  JSOP_POP,
+/*24*/  JSOP_POP,
+/*25*/  JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
+/*28*/  JSOP_CALL, 0, 0,
+/*31*/  JSOP_PRIMTOP,
+/*32*/  JSOP_IMACOP,
+/*33*/  JSOP_STOP,
+    },
+};
+static struct {
     jsbytecode apply0[8];
     jsbytecode apply1[12];
     jsbytecode apply2[16];
     jsbytecode apply3[21];
     jsbytecode apply4[26];
     jsbytecode apply5[31];
     jsbytecode apply6[36];
     jsbytecode apply7[41];
@@ -484,18 +508,18 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     3,  /* JSOP_URSH */
     3,  /* JSOP_ADD */
     3,  /* JSOP_SUB */
     3,  /* JSOP_MUL */
     3,  /* JSOP_DIV */
     3,  /* JSOP_MOD */
     0,  /* JSOP_NOT */
     0,  /* JSOP_BITNOT */
-    0,  /* JSOP_NEG */
-    0,  /* JSOP_NEW */
+    2,  /* JSOP_NEG */
+    2,  /* JSOP_POS */
     0,  /* JSOP_DELNAME */
     0,  /* JSOP_DELPROP */
     0,  /* JSOP_DELELEM */
     0,  /* JSOP_TYPEOF */
     0,  /* JSOP_VOID */
     0,  /* JSOP_INCNAME */
     0,  /* JSOP_INCPROP */
     0,  /* JSOP_INCELEM */
@@ -532,17 +556,17 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_NULLTHIS */
     0,  /* JSOP_ITER */
     0,  /* JSOP_NEXTITER */
     0,  /* JSOP_ENDITER */
     7,  /* JSOP_APPLY */
     0,  /* JSOP_SWAP */
     0,  /* JSOP_OBJECT */
     0,  /* JSOP_POP */
-    0,  /* JSOP_POS */
+    0,  /* JSOP_NEW */
     0,  /* JSOP_TRAP */
     0,  /* JSOP_GETARG */
     0,  /* JSOP_SETARG */
     0,  /* JSOP_GETLOCAL */
     0,  /* JSOP_SETLOCAL */
     0,  /* JSOP_UINT16 */
     0,  /* JSOP_NEWINIT */
     0,  /* JSOP_ENDINIT */
@@ -652,17 +676,17 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_CALLELEM */
     0,  /* JSOP_STOP */
     0,  /* JSOP_GETXPROP */
     0,  /* JSOP_CALLXMLNAME */
     0,  /* JSOP_TYPEOFEXPR */
     0,  /* JSOP_ENTERBLOCK */
     0,  /* JSOP_LEAVEBLOCK */
     0,  /* JSOP_PICK */
-    0,  /* JSOP_UNUSED202 */
+    0,  /* JSOP_PRIMTOP */
     0,  /* JSOP_UNUSED203 */
     0,  /* JSOP_UNUSED204 */
     0,  /* JSOP_UNUSED205 */
     0,  /* JSOP_UNUSED206 */
     0,  /* JSOP_UNUSED207 */
     0,  /* JSOP_UNUSED208 */
     0,  /* JSOP_UNUSED209 */
     0,  /* JSOP_GENERATOR */
--- a/js/src/imacros.jsasm
+++ b/js/src/imacros.jsasm
@@ -204,16 +204,41 @@ 6:      pop                             
 7:      swap                            # lval rval obj
         pop                             # lval rval
 8:      add                             # aval
         stop
     .end
 
 .end
 
+.igroup unary JSOP_NEG-JSOP_POS
+
+    .imacro sign                        # obj
+        dup                             # obj obj
+        dup                             # obj obj obj
+        getprop valueOf                 # obj obj valueOf
+        ifprimtop 2                     # obj obj valueOf
+        swap                            # obj valueOf obj
+        call 0                          # obj lval
+        ifprimtop 1                     # obj lval
+        goto 3                          # obj lval
+1:      swap                            # lval obj
+        pop                             # lval
+        goto 4                          # lval
+2:      pop                             # obj obj
+3:      pop                             # obj
+        callprop toString               # toString obj
+        call 0                          # lval
+        primtop                         # lval
+4:      imacop                          # aval
+        stop
+    .end
+
+.end
+
 .igroup apply JSOP_APPLY
     .imacro apply0                          # apply fun this arr
         pick 3                              # fun this arr apply
         pop                                 # fun this arr
         pop                                 # fun this
         call 0                              #
         stop                                #
     .end                                    #
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -6392,16 +6392,27 @@ js_Interpret(JSContext *cx)
             JS_ASSERT(regs.sp > StackBase(fp));
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_PRIMITIVE(rval)) {
                 len = GET_JUMP_OFFSET(regs.pc);
                 BRANCH(len);
             }
           END_CASE(JSOP_IFPRIMTOP)
 
+          BEGIN_CASE(JSOP_PRIMTOP)
+            JS_ASSERT(regs.sp > StackBase(fp));
+            lval = FETCH_OPND(-1);
+            if (!JSVAL_IS_PRIMITIVE(lval)) {
+                js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
+                                    JSDVG_SEARCH_STACK, lval, NULL,
+                                    "primitive type");
+                goto error;
+            }
+          END_CASE(JSOP_PRIMTOP)
+
           BEGIN_CASE(JSOP_INSTANCEOF)
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_PRIMITIVE(rval) ||
                 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
                 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                                     -1, rval, NULL);
                 goto error;
             }
@@ -6843,17 +6854,16 @@ js_Interpret(JSContext *cx)
           L_JSOP_TOATTRNAME:
           L_JSOP_QNAME:
           L_JSOP_QNAMECONST:
           L_JSOP_QNAMEPART:
           L_JSOP_ANYNAME:
           L_JSOP_DEFXMLNS:
 # endif
 
-          L_JSOP_UNUSED202:
           L_JSOP_UNUSED203:
           L_JSOP_UNUSED204:
           L_JSOP_UNUSED205:
           L_JSOP_UNUSED206:
           L_JSOP_UNUSED207:
           L_JSOP_UNUSED208:
           L_JSOP_UNUSED209:
           L_JSOP_UNUSED219:
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -142,17 +142,17 @@ OPDEF(JSOP_URSH,      26, "ursh",       
 OPDEF(JSOP_ADD,       27, "add",        "+",          1,  2,  1, 13,  JOF_BYTE|JOF_LEFTASSOC)
 OPDEF(JSOP_SUB,       28, "sub",        "-",          1,  2,  1, 13,  JOF_BYTE|JOF_LEFTASSOC)
 OPDEF(JSOP_MUL,       29, "mul",        "*",          1,  2,  1, 14,  JOF_BYTE|JOF_LEFTASSOC)
 OPDEF(JSOP_DIV,       30, "div",        "/",          1,  2,  1, 14,  JOF_BYTE|JOF_LEFTASSOC)
 OPDEF(JSOP_MOD,       31, "mod",        "%",          1,  2,  1, 14,  JOF_BYTE|JOF_LEFTASSOC)
 OPDEF(JSOP_NOT,       32, "not",        "!",          1,  1,  1, 15,  JOF_BYTE|JOF_DETECTING)
 OPDEF(JSOP_BITNOT,    33, "bitnot",     "~",          1,  1,  1, 15,  JOF_BYTE)
 OPDEF(JSOP_NEG,       34, "neg",        "- ",         1,  1,  1, 15,  JOF_BYTE)
-OPDEF(JSOP_NEW,       35, js_new_str,   NULL,         3, -1,  1, 17,  JOF_UINT16|JOF_INVOKE)
+OPDEF(JSOP_POS,       35, "pos",        "+ ",         1,  1,  1, 15,  JOF_BYTE)
 OPDEF(JSOP_DELNAME,   36, "delname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEL)
 OPDEF(JSOP_DELPROP,   37, "delprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_DEL)
 OPDEF(JSOP_DELELEM,   38, "delelem",    NULL,         1,  2,  1, 15,  JOF_BYTE |JOF_ELEM|JOF_DEL)
 OPDEF(JSOP_TYPEOF,    39, js_typeof_str,NULL,         1,  1,  1, 15,  JOF_BYTE|JOF_DETECTING)
 OPDEF(JSOP_VOID,      40, js_void_str,  NULL,         1,  1,  1, 15,  JOF_BYTE)
 
 OPDEF(JSOP_INCNAME,   41, "incname",    NULL,         3,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
 OPDEF(JSOP_INCPROP,   42, "incprop",    NULL,         3,  1,  1, 15,  JOF_ATOM|JOF_PROP|JOF_INC|JOF_TMPSLOT2)
@@ -217,17 +217,17 @@ OPDEF(JSOP_SWAP,      79, "swap",       
 
 /* Push object literal. */
 OPDEF(JSOP_OBJECT,    80, "object",     NULL,         3,  0,  1, 19,  JOF_OBJECT)
 
 /* Pop value and discard it. */
 OPDEF(JSOP_POP,       81, "pop",        NULL,         1,  1,  0,  2,  JOF_BYTE)
 
 /* Convert value to number, for unary +. */
-OPDEF(JSOP_POS,       82, "pos",        "+ ",         1,  1,  1, 15,  JOF_BYTE)
+OPDEF(JSOP_NEW,       82, js_new_str,   NULL,         3, -1,  1, 17,  JOF_UINT16|JOF_INVOKE)
 
 /* Trap into debugger for breakpoint, etc. */
 OPDEF(JSOP_TRAP,      83, "trap",       NULL,         1,  0,  0,  0,  JOF_BYTE)
 
 /* Fast get/set ops for function arguments and local variables. */
 OPDEF(JSOP_GETARG,    84, "getarg",     NULL,         3,  0,  1, 19,  JOF_QARG |JOF_NAME)
 OPDEF(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  3,  JOF_QARG |JOF_NAME|JOF_SET)
 OPDEF(JSOP_GETLOCAL,  86,"getlocal",    NULL,         3,  0,  1, 19,  JOF_LOCAL|JOF_NAME)
@@ -485,17 +485,20 @@ OPDEF(JSOP_TYPEOFEXPR,    198,"typeofexp
 OPDEF(JSOP_ENTERBLOCK,    199,"enterblock",  NULL,    3,  0, -1,  0,  JOF_OBJECT)
 OPDEF(JSOP_LEAVEBLOCK,    200,"leaveblock",  NULL,    3, -1,  0,  0,  JOF_UINT16)
 
 /*
  * Pick an element from the stack.
  */
 OPDEF(JSOP_PICK,          201,"pick",        NULL,    2,  1,  0,  0,  JOF_UINT8)
 
-OPDEF(JSOP_UNUSED202,     202,"unused202",   NULL,    1,  0,  0,  0,  JOF_BYTE)
+
+/* Throws a TypeError if the value at the top of the stack is not primitive. */
+OPDEF(JSOP_PRIMTOP,       202, "primtop",    NULL,    1,  1,  1,  0,  JOF_BYTE)
+
 OPDEF(JSOP_UNUSED203,     203,"unused203",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED204,     204,"unused204",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED205,     205,"unused205",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED206,     206,"unused206",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED207,     207,"unused207",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED208,     208,"unused208",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED209,     209,"unused209",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -5820,16 +5820,20 @@ TraceRecorder::record_JSOP_BITNOT()
 {
     return unary(LIR_not);
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_NEG()
 {
     jsval& v = stackval(-1);
+
+    if (!JSVAL_IS_PRIMITIVE(v))
+        return call_imacro(unary_imacros.sign);
+
     if (isNumber(v)) {
         LIns* a = get(&v);
 
         /* If we're a promoted integer, we have to watch out for 0s since -0 is a double.
            Only follow this path if we're not an integer that's 0 and we're not a double 
            that's zero.
          */
         if (isPromoteInt(a) &&
@@ -5842,17 +5846,65 @@ TraceRecorder::record_JSOP_NEG()
             a = lir->ins1(LIR_i2f, a);
         } else {
             a = lir->ins1(LIR_fneg, a);
         }
 
         set(&v, a);
         return true;
     }
-    return false;
+
+    if (JSVAL_IS_NULL(v)) {
+        jsdpun u;
+        u.d = -0.0;
+        set(&v, lir->insImmq(u.u64));
+        return true;
+    }
+
+    LIns* args[] = { get(&v), cx_ins };
+    if (JSVAL_IS_STRING(v)) {
+        set(&v, lir->ins1(LIR_fneg, lir->insCall(&js_StringToNumber_ci, args)));
+        return true;
+    }
+
+    JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN);
+    set(&v, lir->ins1(LIR_fneg, lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args)));
+    return true;
+}
+
+JS_REQUIRES_STACK bool
+TraceRecorder::record_JSOP_POS()
+{
+    jsval& r = stackval(-1);
+
+    if (!JSVAL_IS_PRIMITIVE(r))
+        return call_imacro(unary_imacros.sign);
+
+    if (isNumber(r))
+        return true;
+
+    if (JSVAL_IS_NULL(r)) {
+        set(&r, lir->insImmq(0));
+        return true;
+    }
+
+    LIns* args[] = { get(&r), cx_ins };
+    set(&r, lir->insCall(JSVAL_IS_STRING(r)
+                         ? &js_StringToNumber_ci
+                         : &js_BooleanOrUndefinedToNumber_ci,
+                         args));
+    return true;
+}
+
+JS_REQUIRES_STACK bool
+TraceRecorder::record_JSOP_PRIMTOP()
+{
+    // Either this opcode does nothing or we couldn't have traced here, because
+    // we'd have thrown an exception -- so do nothing if we actually hit this.
+    return true;
 }
 
 JSBool
 js_Array(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
 
 JSBool
 js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 
@@ -7110,23 +7162,16 @@ TraceRecorder::record_JSOP_OBJECT()
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_POP()
 {
     return true;
 }
 
 JS_REQUIRES_STACK bool
-TraceRecorder::record_JSOP_POS()
-{
-    jsval& r = stackval(-1);
-    return isNumber(r);
-}
-
-JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_TRAP()
 {
     return false;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_GETARG()
 {
@@ -8562,21 +8607,28 @@ InitIMacroCode()
         imacro_code[op] = (jsbytecode*)&binary_imacros - 1;
 
     // NB: above loop mis-set JSOP_ADD's entry, so order here is crucial.
     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;
-}
-
-#define UNUSED(n) JS_REQUIRES_STACK bool TraceRecorder::record_JSOP_UNUSED##n() { return false; }
-
-UNUSED(202)
+
+    imacro_code[JSOP_NEG] = (jsbytecode*)&unary_imacros - 1;
+    imacro_code[JSOP_POS] = (jsbytecode*)&unary_imacros - 1;
+}
+
+#define UNUSED(n)                                                             \
+    JS_REQUIRES_STACK bool                                                    \
+    TraceRecorder::record_JSOP_UNUSED##n() {                                  \
+        JS_NOT_REACHED("JSOP_UNUSED" # n);                                    \
+        return false;                                                         \
+    }
+
 UNUSED(203)
 UNUSED(204)
 UNUSED(205)
 UNUSED(206)
 UNUSED(207)
 UNUSED(208)
 UNUSED(209)
 UNUSED(219)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -474,16 +474,17 @@ public:
 # include "jsopcode.tbl"
 #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))
 
 /*
  * See jsinterp.cpp for the ENABLE_TRACER definition. Also note how comparing x
  * to JSOP_* constants specializes trace-recording code at compile time either
  * to include imacro support, or exclude it altogether for this particular x.
  *
  * We save macro-generated code size also via bool TraceRecorder::record_JSOP_*
  * return type, instead of a three-state: OK, ABORTED, IMACRO_STARTED. But the
@@ -494,17 +495,18 @@ public:
 #define RECORD_ARGS(x,args)                                                   \
     JS_BEGIN_MACRO                                                            \
         if (!js_MonitorRecording(TRACE_RECORDER(cx))) {                       \
             ENABLE_TRACER(0);                                                 \
         } else {                                                              \
             TRACE_ARGS_(x, args,                                              \
                 if ((fp->flags & JSFRAME_IMACRO_START) &&                     \
                     (x == JSOP_ITER || x == JSOP_NEXTITER ||                  \
-                     x == JSOP_APPLY || JSOP_IS_BINARY(x))) {                 \
+                     x == JSOP_APPLY || JSOP_IS_BINARY(x) ||                  \
+                     JSOP_IS_UNARY(op))) {                                    \
                     fp->flags &= ~JSFRAME_IMACRO_START;                       \
                     atoms = COMMON_ATOMS_START(&rt->atomState);               \
                     op = JSOp(*regs.pc);                                      \
                     DO_OP();                                                  \
                 }                                                             \
             );                                                                \
          }                                                                    \
     JS_END_MACRO
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 37)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 38)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -3570,16 +3570,53 @@ function testCallPick() {
 
     for (var i = 0; i < 10; ++i)
         g.call(this, z[i], "");
     return true;
 }
 testCallPick.expected = true;
 test(testCallPick);
 
+function testInvertNullAfterNegateNull()
+{
+  for (var i = 0; i < 5; i++) !null;
+  for (var i = 0; i < 5; i++) -null;
+  return "no assertion";
+}
+testInvertNullAfterNegateNull.expected = "no assertion";
+test(testInvertNullAfterNegateNull);
+
+function testUnaryImacros()
+{
+  function checkArg(x)
+  {
+    return 1;
+  }
+
+  var o = { valueOf: checkArg, toString: null };
+  var count = 0;
+  var v = 0;
+  for (var i = 0; i < 5; i++)
+    v += +o + -(-o);
+
+  var results = [v === 10 ? "valueOf passed" : "valueOf failed"];
+
+  o.valueOf = null;
+  o.toString = checkArg;
+
+  for (var i = 0; i < 5; i++)
+    v += +o + -(-o);
+
+  results.push(v === 20 ? "toString passed" : "toString failed");
+
+  return results.join(", ");
+}
+testUnaryImacros.expected = "valueOf passed, toString passed";
+test(testUnaryImacros);
+
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *
  * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *