Closed Bug 465255 Opened 16 years ago Closed 16 years ago

TM: Rewrite ::cmp

Categories

(Core :: JavaScript Engine, defect)

x86
macOS
defect
Not set
normal

Tracking

()

VERIFIED FIXED

People

(Reporter: gal, Assigned: Waldo)

References

Details

(Keywords: verified1.9.1, Whiteboard: fixed-in-tracemonkey)

Attachments

(1 file, 2 obsolete files)

cmp() is a total mess. Split it up into eq and cmp using a chain of functions for the latter steps, like emitCmp for the part thats in the if (!x) part, and finally emitGuard for the last step. Implement eq and cmp using the same structure and stemps as the spec, step by step. Right now we cover only a subset, and even that with bugs. Nominating Waldo for this. If you don't like it assign it back to me.
FYI, I'm spending time writing a basically-exhaustive testcase to exercise all the comparison operators before I actually work on a patch.
Status: NEW → ASSIGNED
Good starting point. I think trace-tests has already a generator for some combination of operators for this. Maybe you can borrow from there.
Attached patch Thorough tests (obsolete) — Splinter Review
Here are the tests I'll be working against.
All the tests in the previous attachment pass with this patch.  However, for some reason fannkuch in trace-test.js is ilooping, so this is most but not all the way there.  Still investigating that...

We could be cleverer here if undefined and null were their own types, grumble grumble.  Who really uses integers in the range ([-2**30, -2**28 - 1] U [2**28, 2**30 - 1]) anyway?  For my purposes undefined and null are similar enough that we could probably dump them in their own type tag without impairing many type-based optimizations here, for what it's worth, if we'd prefer to only free up one tag value.
Flags: blocking1.9.1+
Oh, just in case I forget later, tracing test fodder, although not sure how to fit it in with existing tests given that it mucks with Object.prototype.valueOf:

var x = {}, y = {}; delete Object.prototype.valueOf; for (var i = 0; i < 5; i++) (x < y)
To better match the interpreter, how about calling the methods names such as relational, equality, strictEquality?

/be
Sounds good to me.
Blocks: 465236
Presently if you do a relational comparison such as null <= null on this, it aborts the trace with "Object.prototype.valueOf deleted, no tracing for you!". This is not strictly accurate, and could be improved-on.

I'm a little surprised that the relational algorithm here is so divergent from the spec-ese in E262-3. I implemented it a number of times last year in the ES4 interpreter and found that every attempt I made to refactor into a slightly different control structure failed, wound up having to stick quite close to the letter of the law. I'd recommend the same.
(In reply to comment #8)
> Presently if you do a relational comparison such as null <= null on this, it
> aborts the trace with "Object.prototype.valueOf deleted, no tracing for you!".
> This is not strictly accurate, and could be improved-on.

Yeah, a more recent version fixes that; I noticed the inaccuracy when writing a test that would have deleted Object.prototype.valueOf, where I noticed I just needed the value to not be a function instead of entirely absent.

> I'm a little surprised that the relational algorithm here is so divergent from
> the spec-ese in E262-3. I implemented it a number of times last year in the
> ES4 interpreter and found that every attempt I made to refactor into a
> slightly different control structure failed, wound up having to stick quite
> close to the letter of the law. I'd recommend the same.

It's mostly a straight conversion from the existing code; I probably will attempt to push it closer to spec-steps before declaring it ready for a review.

(In reply to bug 465236 comment #1)
> It would be better if that bug actually performed the null <= null comparison,
> but at least it manages to abort the trace rather than fault.

Such comparisons are presumably rare; note that since null and object occupy the same type, special-casing this would slow down object comparisons and make the code complex for little real benefit.  If bug 457363 were fixed such that null-specific checks didn't interfere with object code, it would make more sense to do this.
There's a part of me that still really wishes ::equality were more spec-steppy, but the problem is the abstract equality comparison algorithm is so recursive that I don't think it's particularly easy to make that work.  I think leaning on the tests is really the way to go here.
Attachment #348727 - Attachment is obsolete: true
Attachment #349313 - Attachment is obsolete: true
Attachment #351040 - Flags: review?(gal)
Attachment #351040 - Flags: review?(gal) → review+
Comment on attachment 351040 [details] [diff] [review]
Passes all tests, ::relational more spec-steppy, includes tests

>Bug 465255 - Rewrite TraceRecorder::cmp.  NOT REVIEWED YET
>
>diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp
>--- a/js/src/jstracer.cpp
>+++ b/js/src/jstracer.cpp
>@@ -1,4 +1,4 @@
>-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
>+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
>  * vim: set ts=4 sw=4 et tw=99:
>  *
>  * ***** BEGIN LICENSE BLOCK *****
>@@ -4533,8 +4533,45 @@ static struct {
> 
> JS_STATIC_ASSERT(sizeof(binary_imacros) < IMACRO_PC_ADJ_LIMIT);
> 
>-bool
>-TraceRecorder::cmp(LOpcode op, int flags)
>+void
>+TraceRecorder::strictEquality(bool equal)
>+{
>+    jsval& r = stackval(-1);
>+    jsval& l = stackval(-2);
>+    LIns* l_ins = get(&l);
>+    LIns* r_ins = get(&r);
>+
>+    uint8 ltag = getPromotedType(l);
>+    if (ltag != getPromotedType(r)) {
>+        set(&l, lir->insImm(!equal));
>+        return;
>+    }
>+
>+    LIns* x;
>+    if (ltag == JSVAL_STRING) {
>+        LIns* args[] = { r_ins, l_ins };
>+        /*
>+         * FIXME: js_EqualStrings returns 0/1, so we really shouldn't have to
>+         *        always compare to 0.  The problem is there are assertions that
>+         *        require the instruction here to be a condition; this is easily
>+         *        fixable by stealing a bit from CallInfo to return
>+         *        condition-ness.
>+         */

File a bug and put the bug number into the comment?

>+        x = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
>+        if (!equal)
>+            x = lir->ins_eq0(x);
>+    } else {
>+        LOpcode op = (ltag != JSVAL_DOUBLE) ? LIR_eq : LIR_feq;
>+        x = lir->ins2(op, l_ins, r_ins);

This ...

>+        if (!equal)
>+            x = lir->ins_eq0(x);

Move out of the if and share between the 2 cases?

>+    }
>+
>+    set(&l, x);
>+}
>+
>+bool
>+TraceRecorder::equality(int flags)
> {
>     jsval& r = stackval(-1);
>     jsval& l = stackval(-2);
>@@ -4545,28 +4582,10 @@ TraceRecorder::cmp(LOpcode op, int flags
>     LIns* r_ins = get(&r);
>     bool fp = false;
> 
>-    if (op != LIR_feq) {
>-        if (JSVAL_IS_OBJECT(l) && hasValueOfMethod(l)) {
>-            if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
>-                return call_imacro(binary_imacros.obj_obj);
>-            return call_imacro(binary_imacros.obj_any);
>-        }
>-        if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
>-            return call_imacro(binary_imacros.any_obj);
>-    }
>-
>-    // CMP_STRICT is set only for JSOP_STRICTEQ and JSOP_STRICTNE, which correspond to the
>-    // === and !== operators. negate is true for !== and false for ===. The strict equality
>-    // operators produce false if the types of the operands differ, i.e. if only one of 
>-    // them is a number. 
>-    if ((flags & CMP_STRICT) && getPromotedType(l) != getPromotedType(r)) {
>-        x = INS_CONST(negate);
>-        cond = negate;
>-    } else if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
>+    if (JSVAL_IS_STRING(l) || JSVAL_IS_STRING(r)) {
>         // Comparing equality of a string against null always produces false.
>-        if (op == LIR_feq &&
>-            ((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
>-             (JSVAL_IS_NULL(r) && r_ins->isconst()))) {
>+        if ((JSVAL_IS_NULL(l) && l_ins->isconst()) ||
>+            (JSVAL_IS_NULL(r) && r_ins->isconst())) {
>             x = INS_CONST(negate);
>             cond = negate;
>         } else {
>@@ -4574,12 +4593,9 @@ TraceRecorder::cmp(LOpcode op, int flags
>                 ABORT_TRACE("unsupported type for cmp vs string");
> 
>             LIns* args[] = { r_ins, l_ins };
>-            if (op == LIR_feq)
>-                l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
>-            else
>-                l_ins = lir->insCall(&js_CompareStrings_ci, args);
>+            l_ins = lir->ins_eq0(lir->insCall(&js_EqualStrings_ci, args));
>             r_ins = lir->insImm(0);
>-            cond = evalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
>+            cond = js_EqualStrings(JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
>         }
>     } else if (isNumber(l) || isNumber(r)) {
>         jsval tmp[2] = {l, r};
>@@ -4593,7 +4609,7 @@ TraceRecorder::cmp(LOpcode op, int flags
>         LIns* args[] = { l_ins, cx_ins };
>         if (l == JSVAL_NULL && l_ins->isconst()) {
>             jsdpun u;
>-            u.d = (op == LIR_feq) ? js_NaN : 0.0;
>+            u.d = js_NaN;
>             l_ins = lir->insImmq(u.u64);
>         } else if (JSVAL_IS_STRING(l)) {
>             l_ins = lir->insCall(&js_StringToNumber_ci, args);
>@@ -4615,7 +4631,7 @@ TraceRecorder::cmp(LOpcode op, int flags
>         args[1] = cx_ins;
>         if (r == JSVAL_NULL && r_ins->isconst()) {
>             jsdpun u;
>-            u.d = (op == LIR_feq) ? js_NaN : 0.0;
>+            u.d = js_NaN;
>             r_ins = lir->insImmq(u.u64);
>         } else if (JSVAL_IS_STRING(r)) {
>             r_ins = lir->insCall(&js_StringToNumber_ci, args);
>@@ -4626,21 +4642,13 @@ TraceRecorder::cmp(LOpcode op, int flags
>             ABORT_TRACE("unsupported RHS type for cmp vs number");
>         }
>         rnum = js_ValueToNumber(cx, &tmp[1]);
>-        cond = evalCmp(op, lnum, rnum);
>-    } else if ((JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
>+        cond = (lnum == rnum);
>+    } else if (JSVAL_TAG(l) == JSVAL_BOOLEAN && JSVAL_TAG(r) == JSVAL_BOOLEAN) {
>         // The well-known values of JSVAL_TRUE and JSVAL_FALSE make this very easy.
>         // In particular: JSVAL_TO_BOOLEAN(0) < JSVAL_TO_BOOLEAN(1) so all of these comparisons do
>         // the right thing.
>-        cond = evalCmp(op, l, r);
>-        // For ==, !=, ===, and !=== the result is magically correct even if undefined (2) is
>-        // involved. For the relational operations we need some additional cmov magic to make
>-        // the result always false (since undefined becomes NaN per ECMA and that doesn't
>-        // compare to anything, even itself). The code for this is emitted a few lines down.
>+        cond = (l == r);
>     } else if (JSVAL_IS_OBJECT(l) && JSVAL_IS_OBJECT(r)) {
>-        if (op != LIR_feq) {
>-            JS_NOT_REACHED("we should have converted to numbers already");
>-            return false;
>-        }
>         cond = (l == r); 
>     } else {
>         ABORT_TRACE("unsupported operand types for cmp");
>@@ -4649,52 +4657,156 @@ TraceRecorder::cmp(LOpcode op, int flags
>     /* 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. */
>-        if (!fp) {
>-            JS_ASSERT(op >= LIR_feq && op <= LIR_fge);
>-            op = LOpcode(op + (LIR_eq - LIR_feq));
>-        }
>+        LOpcode op = fp ? LIR_feq : LIR_eq;
>         x = lir->ins2(op, l_ins, r_ins);
>         if (negate) {
>             x = lir->ins_eq0(x);
>             cond = !cond;
>         }
>-        // For boolean comparison we need a bit post-processing to make the result false if
>-        // either side is undefined.
>-        if (op != LIR_eq && (JSVAL_TAG(l) == JSVAL_BOOLEAN) && (JSVAL_TAG(r) == JSVAL_BOOLEAN)) {
>-            x = lir->ins_choose(lir->ins2i(LIR_eq, 
>-                                           lir->ins2i(LIR_and, 
>-                                                      lir->ins2(LIR_or, l_ins, r_ins),
>-                                                      JSVAL_TO_BOOLEAN(JSVAL_VOID)),
>-                                           JSVAL_TO_BOOLEAN(JSVAL_VOID)),
>-                                lir->insImm(JSVAL_TO_BOOLEAN(JSVAL_FALSE)),
>-                                x);
>-            x = lir->ins_eq0(lir->ins_eq0(x));
>-            if ((l == JSVAL_VOID) || (r == JSVAL_VOID))
>-                cond = false;
>-        }
>-    }
>-    
>-    /* Don't guard if the same path is always taken. */
>-    if (!x->isconst()) {
>-        if (flags & CMP_CASE) {
>+    }
>+    
>+    if (flags & CMP_CASE) {
>+        /* Only guard if the same path may not always be taken. */
>+        if (!x->isconst())
>             guard(cond, x, BRANCH_EXIT);
>-            return true;
>-        }
>-
>-        /* The interpreter fuses comparisons and the following branch,
>-           so we have to do that here as well. */
>-        if (flags & CMP_TRY_BRANCH_AFTER_COND) {
>-            fuseIf(cx->fp->regs->pc + 1, cond, x);
>-        }
>-    } else if (flags & CMP_CASE) {
>-        return true;
>-    }
>-
>-    /* 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. */
>+        return true;
>+    }
>+
>+    /*
>+     * 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 ((flags & CMP_TRY_BRANCH_AFTER_COND) && !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);
>+    return true;
>+}
>+
>+bool
>+TraceRecorder::relational(LOpcode op, int flags)
>+{
>+    jsval& r = stackval(-1);
>+    jsval& l = stackval(-2);
>+    LIns* x = NULL;
>+    bool negate = !!(flags & CMP_NEGATE);
>+    bool cond;
>+    LIns* l_ins = get(&l);
>+    LIns* r_ins = get(&r);
>+    bool fp = false;
>+
>+    /*
>+     * 11.8.5 if either argument is an object with a function-valued valueOf
>+     * property; if both arguments are objects with non-function-valued valueOf
>+     * properties, abort.
>+     */
>+    if (JSVAL_IS_OBJECT(l) && hasValueOfMethod(l)) {
>+        if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
>+            return call_imacro(binary_imacros.obj_obj);
>+        return call_imacro(binary_imacros.obj_any);
>+    }
>+    if (JSVAL_IS_OBJECT(r) && hasValueOfMethod(r))
>+        return call_imacro(binary_imacros.any_obj);
>+    if (JSVAL_IS_OBJECT(l) || JSVAL_IS_OBJECT(r))
>+        ABORT_TRACE("comparing two objects with non-function valueOf");
>+
>+    /* 11.8.5 steps 3, 16-21. */
>+    if (JSVAL_IS_STRING(l) && JSVAL_IS_STRING(r)) {
>+        LIns* args[] = { r_ins, l_ins };
>+        l_ins = lir->insCall(&js_CompareStrings_ci, args);
>+        r_ins = lir->insImm(0);
>+        cond = evalCmp(op, JSVAL_TO_STRING(l), JSVAL_TO_STRING(r));
>+        goto do_comparison;
>+    }
>+
>+    /* 11.8.5 steps 4-5. */
>+    jsval lnum = l;
>+    jsval rnum = r;
>+    if (!JSVAL_IS_NUMBER(l)) {
>+        jsval tmp = l;
>+        LIns* args[] = { l_ins, cx_ins };
>+        switch (JSVAL_TAG(l)) {
>+          case JSVAL_BOOLEAN:
>+            l_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
>+            break;

No empty line here for simple enough switch statements.

>+
>+          case JSVAL_STRING:
>+            l_ins = lir->insCall(&js_StringToNumber_ci, args);
>+            break;

Here no empty line either.

>+
>+          case JSVAL_INT:
>+          case JSVAL_DOUBLE:
>+          case JSVAL_OBJECT:
>+          default:
>+            JS_ASSERT(0);
>+            ABORT_TRACE("safety belt");

Is this supposed to be JS_NOT_REACHED? More descriptive abort message and a comment whats going on here would be good.

>+        }
>+
>+        JSAutoTempValueRooter tvr(cx, tmp);
>+        lnum = js_ValueToNumber(cx, &tmp);
>+    }
>+    if (!JSVAL_IS_NUMBER(r)) {
>+        jsval tmp = r;
>+        LIns* args[] = { r_ins, cx_ins };
>+        switch (JSVAL_TAG(r)) {
>+          case JSVAL_BOOLEAN:
>+            r_ins = lir->insCall(&js_BooleanOrUndefinedToNumber_ci, args);
>+            break;

Empty line...

>+
>+          case JSVAL_STRING:
>+            r_ins = lir->insCall(&js_StringToNumber_ci, args);
>+            break;
>+
>+          case JSVAL_INT:
>+          case JSVAL_DOUBLE:
>+          case JSVAL_OBJECT:
>+          default:
>+            JS_ASSERT(0);
>+            ABORT_TRACE("safety belt");
>+        }
>+
>+        JSAutoTempValueRooter tvr(cx, tmp);
>+        rnum = js_ValueToNumber(cx, &tmp);
>+    }
>+    cond = evalCmp(op, lnum, rnum);
>+    fp = true;
>+
>+    /* 11.8.5 steps 6-15. */
>+  do_comparison:
>+    /* If the result is not a number or it's not a quad, we must use an integer compare. */
>+    if (!fp) {
>+        JS_ASSERT(op >= LIR_feq && op <= LIR_fge);
>+        op = LOpcode(op + (LIR_eq - LIR_feq));
>+    }
>+    /* Take advantage of (<=)^3 == (>) and (>=)^3 == (<). */

I know this is covered by your asserts somewhere but maybe duplicate that assert here? Its basically
almost as descriptive as the comment above? Up to you but I think it would be nice for future editors of the function.

>+    if (negate) {
>+        x = lir->ins2(LOpcode(op ^ 3), l_ins, r_ins);
>+        cond = !cond;
>+    } else {
>+        x = lir->ins2(op, l_ins, r_ins);
>+    }
>+
>+    /*
>+     * 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 ((flags & CMP_TRY_BRANCH_AFTER_COND) && !x->isconst())
>+        fuseIf(cx->fp->regs->pc + 1, cond, x);

This is redundant with the other cases. Maybe factor out into a function that all of them call at the end to do the fusing and pick the right false/true return value?

>+
>+    /*
>+     * 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);
>     return true;
> }
>@@ -5495,37 +5607,37 @@ bool
> bool
> TraceRecorder::record_JSOP_EQ()
> {
>-    return cmp(LIR_feq, CMP_TRY_BRANCH_AFTER_COND);
>+    return equality(CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
> TraceRecorder::record_JSOP_NE()
> {
>-    return cmp(LIR_feq, CMP_NEGATE | CMP_TRY_BRANCH_AFTER_COND);
>+    return equality(CMP_NEGATE | CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
> TraceRecorder::record_JSOP_LT()
> {
>-    return cmp(LIR_flt, CMP_TRY_BRANCH_AFTER_COND);
>+    return relational(LIR_flt, CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
> TraceRecorder::record_JSOP_LE()
> {
>-    return cmp(LIR_fle, CMP_TRY_BRANCH_AFTER_COND);
>+    return relational(LIR_fle, CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
> TraceRecorder::record_JSOP_GT()
> {
>-    return cmp(LIR_fgt, CMP_TRY_BRANCH_AFTER_COND);
>+    return relational(LIR_fgt, CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
> TraceRecorder::record_JSOP_GE()
> {
>-    return cmp(LIR_fge, CMP_TRY_BRANCH_AFTER_COND);
>+    return relational(LIR_fge, CMP_TRY_BRANCH_AFTER_COND);
> }
> 
> bool
>@@ -6865,13 +6977,15 @@ bool
> bool
> TraceRecorder::record_JSOP_STRICTEQ()
> {
>-    return cmp(LIR_feq, CMP_STRICT);
>+    strictEquality(true);
>+    return true;
> }
> 
> bool
> TraceRecorder::record_JSOP_STRICTNE()
> {
>-    return cmp(LIR_feq, CMP_STRICT | CMP_NEGATE);
>+    strictEquality(false);
>+    return true;
> }
> 
> bool
>@@ -7369,7 +7483,7 @@ bool
> bool
> TraceRecorder::record_JSOP_CASE()
> {
>-    return cmp(LIR_feq, CMP_CASE);
>+    return equality(CMP_CASE);
> }
> 
> bool
>@@ -7562,7 +7676,7 @@ bool
> bool
> TraceRecorder::record_JSOP_CASEX()
> {
>-    return cmp(LIR_feq, CMP_CASE);
>+    return equality(CMP_CASE);
> }
> 
> bool
>diff --git a/js/src/jstracer.h b/js/src/jstracer.h
>--- a/js/src/jstracer.h
>+++ b/js/src/jstracer.h
>@@ -1,4 +1,4 @@
>-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
>+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
>  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
>  *
>  * ***** BEGIN LICENSE BLOCK *****
>@@ -355,8 +355,10 @@ class TraceRecorder : public avmplus::GC
>     bool incElem(jsint incr, bool pre = true);
>     bool incName(jsint incr, bool pre = true);
> 
>-    enum { CMP_NEGATE = 1, CMP_TRY_BRANCH_AFTER_COND = 2, CMP_CASE = 4, CMP_STRICT = 8 };
>-    bool cmp(nanojit::LOpcode op, int flags = 0);
>+    enum { CMP_NEGATE = 1, CMP_TRY_BRANCH_AFTER_COND = 2, CMP_CASE = 4 };
>+    void strictEquality(bool equal);
>+    bool equality(int flags);
>+    bool relational(nanojit::LOpcode op, int flags);
> 
>     bool unary(nanojit::LOpcode op);
>     bool binary(nanojit::LOpcode op);
>diff --git a/js/src/nanojit/LIRopcode.tbl b/js/src/nanojit/LIRopcode.tbl
>--- a/js/src/nanojit/LIRopcode.tbl
>+++ b/js/src/nanojit/LIRopcode.tbl
>@@ -104,9 +104,13 @@ OPDEF(ldcs, 26, 2) // non-volatile 16-bi
> OPDEF(ldcs, 26, 2) // non-volatile 16-bit load
> 
> /*
>- * feq though fge must only be used on float arguments.  They
>- * return integers.  NB: These opcodes must remain continuous
>- * so that comparison-opcode detection works correctly.
>+ * feq though fge must only be used on float arguments.  They return integers.
>+ * For all except feq, (op ^ 1) is the op which flips the
>+ * left and right sides of the comparison, so (lt ^ 1) == gt, or the operator
>+ * "<" is xored with 1 to get ">".  Similarly, (op ^ 3) is the complement of
>+ * op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored
>+ * with 3.  NB: These opcodes must remain continuous so that comparison-opcode
>+ * detection works correctly.
>  */
> OPDEF(feq,      27, 2) // floating-point equality [2 float inputs]
> OPDEF(flt,      28, 2) // floating-point less than: arg1 < arg2
>diff --git a/js/src/trace-test.js b/js/src/trace-test.js
>--- a/js/src/trace-test.js
>+++ b/js/src/trace-test.js
>@@ -1,4 +1,4 @@
>-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
>+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
> /**
>  * A number of the tests in this file depend on the setting of
>  * HOTLOOP.  Define some constants up front, so they're easy to grep
>@@ -2677,6 +2677,996 @@ testIncDec.expected = "0,0,0,0,0,0,1,1,1
> testIncDec.expected = "0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4";
> test(testIncDec);
> 
>+function testComparisons()
>+{
>+  // All the special values from each of the types in
>+  // ECMA-262, 3rd ed. section 8
>+  var undefinedType, nullType, booleanType, stringType, numberType, objectType;
>+
>+  var types = [];
>+  types[undefinedType = 0] = "Undefined";
>+  types[nullType = 1] = "Null";
>+  types[booleanType = 2] = "Boolean";
>+  types[stringType = 3] = "String";
>+  types[numberType = 4] = "Number";
>+  types[objectType = 5] = "Object";
>+
>+  var JSVAL_INT_MIN = -Math.pow(2, 30);
>+  var JSVAL_INT_MAX = Math.pow(2, 30) - 1;
>+
>+  // Values from every ES3 type, hitting all the edge-case and special values
>+  // that can be dreamed up
>+  var values =
>+    {
>+     "undefined":
>+       {
>+         value: function() { return undefined; },
>+         type: undefinedType
>+       },
>+     "null":
>+       {
>+         value: function() { return null; },
>+         type: nullType
>+       },
>+     "true":
>+       {
>+         value: function() { return true; },
>+         type: booleanType
>+       },
>+     "false":
>+       {
>+         value: function() { return false; },
>+         type: booleanType
>+       },
>+     '""':
>+       {
>+         value: function() { return ""; },
>+         type: stringType
>+       },
>+     '"a"':
>+       {
>+         // a > [, for string-object comparisons
>+         value: function() { return "a"; },
>+         type: stringType
>+       },
>+     '"Z"':
>+       {
>+         // Z < [, for string-object comparisons
>+         value: function() { return "Z"; },
>+         type: stringType
>+       },
>+     "0":
>+       {
>+         value: function() { return 0; },
>+         type: numberType
>+       },
>+     "-0":
>+       {
>+         value: function() { return -0; },
>+         type: numberType
>+       },
>+     "1":
>+       {
>+         value: function() { return 1; },
>+         type: numberType
>+       },
>+     "Math.E":
>+       {
>+         value: function() { return Math.E; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MIN - 1":
>+       {
>+         value: function() { return JSVAL_INT_MIN - 1; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MIN":
>+       {
>+         value: function() { return JSVAL_INT_MIN; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MIN + 1":
>+       {
>+         value: function() { return JSVAL_INT_MIN + 1; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MAX - 1":
>+       {
>+         value: function() { return JSVAL_INT_MAX - 1; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MAX":
>+       {
>+         value: function() { return JSVAL_INT_MAX; },
>+         type: numberType
>+       },
>+     "JSVAL_INT_MAX + 1":
>+       {
>+         value: function() { return JSVAL_INT_MAX + 1; },
>+         type: numberType
>+       },
>+     "Infinity":
>+       {
>+         value: function() { return Infinity; },
>+         type: numberType
>+       },
>+     "-Infinity":
>+       {
>+         value: function() { return -Infinity; },
>+         type: numberType
>+       },
>+     "NaN":
>+       {
>+         value: function() { return NaN; },
>+         type: numberType
>+       },
>+     "{}":
>+       {
>+         value: function() { return {}; },
>+         type: objectType
>+       },
>+     "{ valueOf: undefined }":
>+       {
>+         value: function() { return { valueOf: undefined }; },
>+         type: objectType
>+       },
>+     "[]":
>+       {
>+         value: function() { return []; },
>+         type: objectType
>+       },
>+     '[""]':
>+       {
>+         value: function() { return [""]; },
>+         type: objectType
>+       },
>+     '["a"]':
>+       {
>+         value: function() { return ["a"]; },
>+         type: objectType
>+       },
>+     "[0]":
>+       {
>+         value: function() { return [0]; },
>+         type: objectType
>+       }
>+    };
>+
>+  var orderOps =
>+    {
>+     "<": function(a, b) { return a < b; },
>+     ">": function(a, b) { return a > b; },
>+     "<=": function(a, b) { return a <= b; },
>+     ">=": function(a, b) { return a >= b; }
>+    };
>+  var eqOps =
>+    {
>+     "==": function(a, b) { return a == b; },
>+     "!=": function(a, b) { return a != b; },
>+     "===": function(a, b) { return a === b; },
>+     "!==": function(a, b) { return a !== b; }
>+    };
>+
>+
>+  var notEqualIncomparable =
>+    {
>+      eq: { "==": false, "!=": true, "===": false, "!==": true },
>+      order: { "<": false, ">": false, "<=": false, ">=": false }
>+    };
>+  var notEqualLessThan =
>+    {
>+      eq: { "==": false, "!=": true, "===": false, "!==": true },
>+      order: { "<": true, ">": false, "<=": true, ">=": false }
>+    };
>+  var notEqualGreaterThan =
>+    {
>+      eq: { "==": false, "!=": true, "===": false, "!==": true },
>+      order: { "<": false, ">": true, "<=": false, ">=": true }
>+    };
>+  var notEqualNorDifferent =
>+    {
>+      eq: { "==": false, "!=": true, "===": false, "!==": true },
>+      order: { "<": false, ">": false, "<=": true, ">=": true }
>+    };
>+  var strictlyEqual =
>+    {
>+      eq: { "==": true, "!=": false, "===": true, "!==": false },
>+      order: { "<": false, ">": false, "<=": true, ">=": true }
>+    };
>+  var looselyEqual =
>+    {
>+      eq: { "==": true, "!=": false, "===": false, "!==": true },
>+      order: { "<": false, ">": false, "<=": true, ">=": true }
>+    };
>+  var looselyEqualNotDifferent =
>+    {
>+      eq: { "==": true, "!=": false, "===": false, "!==": true },
>+      order: { "<": false, ">": false, "<=": true, ">=": true }
>+    };
>+  var looselyEqualIncomparable =
>+    {
>+      eq: { "==": true, "!=": false, "===": false, "!==": true },
>+      order: { "<": false, ">": false, "<=": false, ">=": false }
>+    };
>+  var strictlyEqualNotDifferent =
>+    {
>+      eq: { "==": true, "!=": false, "===": true, "!==": false },
>+      order: { "<": false, ">": false, "<=": true, ">=": true }
>+    };
>+  var strictlyEqualIncomparable =
>+    {
>+      eq: { "==": true, "!=": false, "===": true, "!==": false },
>+      order: { "<": false, ">": false, "<=": false, ">=": false }
>+    };
>+
>+  var comparingZeroToSomething =
>+    {
>+      "undefined": notEqualIncomparable,
>+      "null": notEqualNorDifferent,
>+      "true": notEqualLessThan,
>+      "false": looselyEqual,
>+      '""': looselyEqualNotDifferent,
>+      '"a"': notEqualIncomparable,
>+      '"Z"': notEqualIncomparable,
>+      "0": strictlyEqual,
>+      "-0": strictlyEqual,
>+      "1": notEqualLessThan,
>+      "Math.E": notEqualLessThan,
>+      "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+      "JSVAL_INT_MIN": notEqualGreaterThan,
>+      "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+      "JSVAL_INT_MAX - 1": notEqualLessThan,
>+      "JSVAL_INT_MAX": notEqualLessThan,
>+      "JSVAL_INT_MAX + 1": notEqualLessThan,
>+      "Infinity": notEqualLessThan,
>+      "-Infinity": notEqualGreaterThan,
>+      "NaN": notEqualIncomparable,
>+      "{}": notEqualIncomparable,
>+      "{ valueOf: undefined }": notEqualIncomparable,
>+      "[]": looselyEqual,
>+      '[""]': looselyEqual,
>+      '["a"]': notEqualIncomparable,
>+      "[0]": looselyEqual
>+    };
>+
>+  var comparingObjectOrObjectWithValueUndefined =
>+    {
>+      "undefined": notEqualIncomparable,
>+      "null": notEqualIncomparable,
>+      "true": notEqualIncomparable,
>+      "false": notEqualIncomparable,
>+      '""': notEqualGreaterThan,
>+      '"a"': notEqualLessThan,
>+      '"Z"': notEqualGreaterThan,
>+      "0": notEqualIncomparable,
>+      "-0": notEqualIncomparable,
>+      "1": notEqualIncomparable,
>+      "Math.E": notEqualIncomparable,
>+      "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+      "JSVAL_INT_MIN": notEqualIncomparable,
>+      "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+      "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+      "JSVAL_INT_MAX": notEqualIncomparable,
>+      "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+      "Infinity": notEqualIncomparable,
>+      "-Infinity": notEqualIncomparable,
>+      "NaN": notEqualIncomparable,
>+      "{}": notEqualNorDifferent,
>+      "{ valueOf: undefined }": notEqualNorDifferent,
>+      "[]": notEqualGreaterThan,
>+      '[""]': notEqualGreaterThan,
>+      '["a"]': notEqualLessThan,
>+      "[0]": notEqualGreaterThan
>+    };
>+
>+  // Constructed expected-value matrix
>+  var expected =
>+    {
>+     "undefined":
>+       {
>+         "undefined": strictlyEqualIncomparable,
>+         "null": looselyEqualIncomparable,
>+         "true": notEqualIncomparable,
>+         "false": notEqualIncomparable,
>+         '""': notEqualIncomparable,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualIncomparable,
>+         "-0": notEqualIncomparable,
>+         "1": notEqualIncomparable,
>+         "Math.E": notEqualIncomparable,
>+         "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+         "JSVAL_INT_MIN": notEqualIncomparable,
>+         "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX": notEqualIncomparable,
>+         "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+         "Infinity": notEqualIncomparable,
>+         "-Infinity": notEqualIncomparable,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualIncomparable,
>+         '[""]': notEqualIncomparable,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualIncomparable
>+       },
>+     "null":
>+       {
>+         "undefined": looselyEqualIncomparable,
>+         "null": strictlyEqualNotDifferent,
>+         "true": notEqualLessThan,
>+         "false": notEqualNorDifferent,
>+         '""': notEqualNorDifferent,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualNorDifferent,
>+         "-0": notEqualNorDifferent,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualNorDifferent,
>+         '[""]': notEqualNorDifferent,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualNorDifferent
>+       },
>+     "true":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": strictlyEqual,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": looselyEqual,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "false":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualNorDifferent,
>+         "true": notEqualLessThan,
>+         "false": strictlyEqual,
>+         '""': looselyEqualNotDifferent,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": looselyEqual,
>+         "-0": looselyEqual,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": looselyEqual,
>+         '[""]': looselyEqual,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": looselyEqual
>+       },
>+     '""':
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualNorDifferent,
>+         "true": notEqualLessThan,
>+         "false": looselyEqual,
>+         '""': strictlyEqual,
>+         '"a"': notEqualLessThan,
>+         '"Z"': notEqualLessThan,
>+         "0": looselyEqual,
>+         "-0": looselyEqual,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualLessThan,
>+         "{ valueOf: undefined }": notEqualLessThan,
>+         "[]": looselyEqual,
>+         '[""]': looselyEqual,
>+         '["a"]': notEqualLessThan,
>+         "[0]": notEqualLessThan
>+       },
>+     '"a"':
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualIncomparable,
>+         "true": notEqualIncomparable,
>+         "false": notEqualIncomparable,
>+         '""': notEqualGreaterThan,
>+         '"a"': strictlyEqual,
>+         '"Z"': notEqualGreaterThan,
>+         "0": notEqualIncomparable,
>+         "-0": notEqualIncomparable,
>+         "1": notEqualIncomparable,
>+         "Math.E": notEqualIncomparable,
>+         "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+         "JSVAL_INT_MIN": notEqualIncomparable,
>+         "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX": notEqualIncomparable,
>+         "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+         "Infinity": notEqualIncomparable,
>+         "-Infinity": notEqualIncomparable,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualGreaterThan,
>+         "{ valueOf: undefined }": notEqualGreaterThan,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': looselyEqualNotDifferent,
>+         "[0]": notEqualGreaterThan
>+       },
>+     '"Z"':
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualIncomparable,
>+         "true": notEqualIncomparable,
>+         "false": notEqualIncomparable,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualLessThan,
>+         '"Z"': strictlyEqual,
>+         "0": notEqualIncomparable,
>+         "-0": notEqualIncomparable,
>+         "1": notEqualIncomparable,
>+         "Math.E": notEqualIncomparable,
>+         "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+         "JSVAL_INT_MIN": notEqualIncomparable,
>+         "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX": notEqualIncomparable,
>+         "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+         "Infinity": notEqualIncomparable,
>+         "-Infinity": notEqualIncomparable,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualLessThan,
>+         "{ valueOf: undefined }": notEqualLessThan,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualLessThan,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "0": comparingZeroToSomething,
>+     "-0": comparingZeroToSomething,
>+     "1":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": looselyEqual,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": strictlyEqual,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "Math.E":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": notEqualGreaterThan,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": notEqualGreaterThan,
>+         "Math.E": strictlyEqual,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "JSVAL_INT_MIN - 1":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualLessThan,
>+         "true": notEqualLessThan,
>+         "false": notEqualLessThan,
>+         '""': notEqualLessThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualLessThan,
>+         "-0": notEqualLessThan,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": strictlyEqual,
>+         "JSVAL_INT_MIN": notEqualLessThan,
>+         "JSVAL_INT_MIN + 1": notEqualLessThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualLessThan,
>+         '[""]': notEqualLessThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualLessThan
>+       },
>+     "JSVAL_INT_MIN":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualLessThan,
>+         "true": notEqualLessThan,
>+         "false": notEqualLessThan,
>+         '""': notEqualLessThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualLessThan,
>+         "-0": notEqualLessThan,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": strictlyEqual,
>+         "JSVAL_INT_MIN + 1": notEqualLessThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualLessThan,
>+         '[""]': notEqualLessThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualLessThan
>+       },
>+     "JSVAL_INT_MIN + 1":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualLessThan,
>+         "true": notEqualLessThan,
>+         "false": notEqualLessThan,
>+         '""': notEqualLessThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualLessThan,
>+         "-0": notEqualLessThan,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": strictlyEqual,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualLessThan,
>+         '[""]': notEqualLessThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualLessThan
>+       },
>+     "JSVAL_INT_MAX - 1":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": notEqualGreaterThan,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": notEqualGreaterThan,
>+         "Math.E": notEqualGreaterThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": strictlyEqual,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "JSVAL_INT_MAX":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": notEqualGreaterThan,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": notEqualGreaterThan,
>+         "Math.E": notEqualGreaterThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX": strictlyEqual,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "JSVAL_INT_MAX + 1":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": notEqualGreaterThan,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": notEqualGreaterThan,
>+         "Math.E": notEqualGreaterThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX": notEqualGreaterThan,
>+         "JSVAL_INT_MAX + 1": strictlyEqual,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "Infinity":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualGreaterThan,
>+         "true": notEqualGreaterThan,
>+         "false": notEqualGreaterThan,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualGreaterThan,
>+         "-0": notEqualGreaterThan,
>+         "1": notEqualGreaterThan,
>+         "Math.E": notEqualGreaterThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX": notEqualGreaterThan,
>+         "JSVAL_INT_MAX + 1": notEqualGreaterThan,
>+         "Infinity": strictlyEqual,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "-Infinity":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualLessThan,
>+         "true": notEqualLessThan,
>+         "false": notEqualLessThan,
>+         '""': notEqualLessThan,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualLessThan,
>+         "-0": notEqualLessThan,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualLessThan,
>+         "JSVAL_INT_MIN": notEqualLessThan,
>+         "JSVAL_INT_MIN + 1": notEqualLessThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": strictlyEqual,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualLessThan,
>+         '[""]': notEqualLessThan,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualLessThan
>+       },
>+     "NaN":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualIncomparable,
>+         "true": notEqualIncomparable,
>+         "false": notEqualIncomparable,
>+         '""': notEqualIncomparable,
>+         '"a"': notEqualIncomparable,
>+         '"Z"': notEqualIncomparable,
>+         "0": notEqualIncomparable,
>+         "-0": notEqualIncomparable,
>+         "1": notEqualIncomparable,
>+         "Math.E": notEqualIncomparable,
>+         "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+         "JSVAL_INT_MIN": notEqualIncomparable,
>+         "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX": notEqualIncomparable,
>+         "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+         "Infinity": notEqualIncomparable,
>+         "-Infinity": notEqualIncomparable,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualIncomparable,
>+         "{ valueOf: undefined }": notEqualIncomparable,
>+         "[]": notEqualIncomparable,
>+         '[""]': notEqualIncomparable,
>+         '["a"]': notEqualIncomparable,
>+         "[0]": notEqualIncomparable
>+       },
>+     "{}": comparingObjectOrObjectWithValueUndefined,
>+     "{ valueOf: undefined }": comparingObjectOrObjectWithValueUndefined,
>+     "[]":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualNorDifferent,
>+         "true": notEqualLessThan,
>+         "false": looselyEqual,
>+         '""': looselyEqual,
>+         '"a"': notEqualLessThan,
>+         '"Z"': notEqualLessThan,
>+         "0": looselyEqual,
>+         "-0": looselyEqual,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualLessThan,
>+         "{ valueOf: undefined }": notEqualLessThan,
>+         "[]": notEqualNorDifferent,
>+         '[""]': notEqualNorDifferent,
>+         '["a"]': notEqualLessThan,
>+         "[0]": notEqualLessThan
>+       },
>+     '[""]':
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualNorDifferent,
>+         "true": notEqualLessThan,
>+         "false": looselyEqual,
>+         '""': looselyEqual,
>+         '"a"': notEqualLessThan,
>+         '"Z"': notEqualLessThan,
>+         "0": looselyEqual,
>+         "-0": looselyEqual,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualLessThan,
>+         "{ valueOf: undefined }": notEqualLessThan,
>+         "[]": notEqualNorDifferent,
>+         '[""]': notEqualNorDifferent,
>+         '["a"]': notEqualLessThan,
>+         "[0]": notEqualLessThan
>+       },
>+     '["a"]':
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualIncomparable,
>+         "true": notEqualIncomparable,
>+         "false": notEqualIncomparable,
>+         '""': notEqualGreaterThan,
>+         '"a"': looselyEqual,
>+         '"Z"': notEqualGreaterThan,
>+         "0": notEqualIncomparable,
>+         "-0": notEqualIncomparable,
>+         "1": notEqualIncomparable,
>+         "Math.E": notEqualIncomparable,
>+         "JSVAL_INT_MIN - 1": notEqualIncomparable,
>+         "JSVAL_INT_MIN": notEqualIncomparable,
>+         "JSVAL_INT_MIN + 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX - 1": notEqualIncomparable,
>+         "JSVAL_INT_MAX": notEqualIncomparable,
>+         "JSVAL_INT_MAX + 1": notEqualIncomparable,
>+         "Infinity": notEqualIncomparable,
>+         "-Infinity": notEqualIncomparable,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualGreaterThan,
>+         "{ valueOf: undefined }": notEqualGreaterThan,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualNorDifferent,
>+         "[0]": notEqualGreaterThan
>+       },
>+     "[0]":
>+       {
>+         "undefined": notEqualIncomparable,
>+         "null": notEqualNorDifferent,
>+         "true": notEqualLessThan,
>+         "false": looselyEqual,
>+         '""': notEqualGreaterThan,
>+         '"a"': notEqualLessThan,
>+         '"Z"': notEqualLessThan,
>+         "0": looselyEqual,
>+         "-0": looselyEqual,
>+         "1": notEqualLessThan,
>+         "Math.E": notEqualLessThan,
>+         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
>+         "JSVAL_INT_MIN": notEqualGreaterThan,
>+         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
>+         "JSVAL_INT_MAX - 1": notEqualLessThan,
>+         "JSVAL_INT_MAX": notEqualLessThan,
>+         "JSVAL_INT_MAX + 1": notEqualLessThan,
>+         "Infinity": notEqualLessThan,
>+         "-Infinity": notEqualGreaterThan,
>+         "NaN": notEqualIncomparable,
>+         "{}": notEqualLessThan,
>+         "{ valueOf: undefined }": notEqualLessThan,
>+         "[]": notEqualGreaterThan,
>+         '[""]': notEqualGreaterThan,
>+         '["a"]': notEqualLessThan,
>+         "[0]": notEqualNorDifferent
>+       }
>+    };
>+  
>+
>+
>+  var failures = [];
>+  function fail(a, ta, b, tb, ex, ac, op)
>+  {
>+    failures.push("(" + a + " " + op + " " + b + ") wrong: " +
>+                  "expected " + ex + ", got " + ac +
>+                  " (types " + types[ta] + ", " + types[tb] + ")");
>+  }
>+
>+  var result = false;
>+  for (var i in values)
>+  {
>+    for (var j in values)
>+    {
>+      // Constants, so hoist to help JIT know that
>+      var vala = values[i], valb = values[j];
>+      var a = vala.value(), b = valb.value();
>+
>+      for (var opname in orderOps)
>+      {
>+        var op = orderOps[opname];
>+        var expect = expected[i][j].order[opname];
>+        var failed = false;
>+
>+        for (var iter = 0; iter < 5; iter++)
>+        {
>+          result = op(a, b);
>+          failed = failed || result !== expect;
>+        }
>+
>+        if (failed)
>+          fail(i, vala.type, j, valb.type, expect, result, opname);
>+      }
>+
>+      for (var opname in eqOps)
>+      {
>+        var op = eqOps[opname];
>+        var expect = expected[i][j].eq[opname];
>+        var failed = false;
>+
>+        for (var iter = 0; iter < 5; iter++)
>+        {
>+          result = op(a, b);
>+          failed = failed || result !== expect;
>+        }
>+
>+        if (failed)
>+          fail(i, vala.type, j, valb.type, expect, result, opname);
>+      }
>+    }
>+  }
>+
>+  if (failures.length == 0)
>+    return "no failures reported!";
>+
>+  return "\n" + failures.join(",\n");
>+}
>+testComparisons.expected = "no failures reported!";
>+test(testComparisons);
>+
> /* NOTE: Keep this test last, since it screws up all for...in loops after it. */
> function testGlobalProtoAccess() {
>     return "ok";

At last we can compare stuff right :)
Landed in tracemonkey, will let sit awhile (and probably merge with m-c) before going to 1.9.1...
Whiteboard: fixed-in-tracemonkey
...or not.  Two obvious errors and one still lurking, after a commit, a followup, and then a full backout.
Whiteboard: fixed-in-tracemonkey
Mochitest runs of the individual failing tests just after the initial checkins confirmed the failures mentioned above.

On Monday I updated my tree and reapplied the patch to find the errors found in the initial landing were no longer present, at least not in isolation running parent directories of the failing tests.  I've tried a few different build configurations thinking that maybe it's a debug/opt thing, but no dice.  I'm now updating to the first failing commit to see if I can debug from there.
Oh, forgot to mention: I CC'd a bunch of people because I wasn't sure where to go without being able to reproduce any failures, then in the meantime remembering I could just up to the bad revision and try from there.  Regardless, if I don't make progress on figuring this out in the rest of today, I will probably need some help debugging this, or at least suggesting good pointers for where to start.
string === string has its condition reversed.  I really really wonder why the test didn't catch this; we trace into other iterations of those loops.  A full run with the fix to that in place passes all tests -- will land later today and finally be done here.
And with that changed, tinderboxen are happy!  We'll give it some time before going to 191.

http://hg.mozilla.org/tracemonkey/rev/56029d4f6305
Whiteboard: fixed-in-tracemonkey
Status: ASSIGNED → RESOLVED
Closed: 16 years ago
Resolution: --- → FIXED
test included in js1_8_1/trace/trace-test.js 
http://hg.mozilla.org/mozilla-central/rev/8f967a7729e2
Flags: in-testsuite+
Flags: in-litmus-
v 1.9.1, 1.9.2
Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: