Move the type level from int/double to number. All traces start out as double in all slots, and denote and promote to/from int as needed. The FuncFilter optimizes on-trace casting and elininates redundant f->i-> chains. More optimization needed on this of course, and this code is now a bit slower than the previous integer-register use. However, this does solve the q += 2.5 issues. The heap access code does not properly cast yet and is likely unstable.
authorAndreas Gal <gal@mozilla.com>
Sun, 06 Jul 2008 15:55:04 -0700
changeset 17469 8374e34d597e56684f05628edb4f5c0bf8752e44
parent 17468 360a6ce57d28f76b5f739770c81c1d0ff57926ce
child 17470 5ebea36d79ade1ab78789c7d79a0bbb3fbfeb1cf
push id1452
push usershaver@mozilla.com
push dateFri, 22 Aug 2008 00:08:22 +0000
treeherderautoland@d13bb0868596 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a1pre
Move the type level from int/double to number. All traces start out as double in all slots, and denote and promote to/from int as needed. The FuncFilter optimizes on-trace casting and elininates redundant f->i-> chains. More optimization needed on this of course, and this code is now a bit slower than the previous integer-register use. However, this does solve the q += 2.5 issues. The heap access code does not properly cast yet and is likely unstable.
js/src/builtins.tbl
js/src/jsbuiltins.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/trace.js
--- a/js/src/builtins.tbl
+++ b/js/src/builtins.tbl
@@ -33,12 +33,13 @@
  * 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 ***** */
 
 BUILTIN2(BoxDouble,             LO, F,  LO,     jsval,     JSContext*, jsdouble,               1, 1)
 BUILTIN2(BoxInt32,              LO, LO, LO,     jsval,     JSContext*, jsint,                  1, 1)
+BUILTIN1(UnboxDouble,           LO,     F,      jsdouble,  jsval,                              1, 1)
 BUILTIN1(UnboxInt32,            LO,     LO,     int32,     jsval,                              1, 1)
 BUILTIN2(dmod,                  F,  F,  F,      jsdouble,  jsdouble, jsdouble,                 1, 1)
 BUILTIN1(doubleToInt32,         F,      LO,     int32,     jsdouble,                           1, 1)
-BUILTIN1(doubleToUint32,        F,      LO,     int32,     jsdouble,                           1, 1)
\ No newline at end of file
+BUILTIN1(doubleToUint32,        F,      LO,     int32,     jsdouble,                           1, 1)
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -67,31 +67,47 @@ FASTCALL jsdouble builtin_dmod(jsdouble 
 }
 
 /* The following boxing/unboxing primitives we can't emit inline because
    they either interact with the GC and depend on Spidermonkey's 32-bit
    integer representation. */
 
 FASTCALL jsval builtin_BoxDouble(JSContext* cx, jsdouble d)
 {
+    jsint i;
+    if (JSDOUBLE_IS_INT(d, i))
+        return INT_TO_JSVAL(i);
     if (!cx->doubleFreeList) /* we must be certain the GC won't kick in */
         return JSVAL_ERROR_COOKIE;
     jsval v; /* not rooted but ok here because we know GC won't run */
     if (!js_NewDoubleInRootedValue(cx, d, &v))
         return JSVAL_ERROR_COOKIE;
     return v;
 }
 
 FASTCALL jsval builtin_BoxInt32(JSContext* cx, jsint i)
 {
     if (JS_LIKELY(INT_FITS_IN_JSVAL(i)))
         return INT_TO_JSVAL(i);
-    return builtin_BoxDouble(cx, (jsdouble)i);
+    if (!cx->doubleFreeList) /* we must be certain the GC won't kick in */
+        return JSVAL_ERROR_COOKIE;
+    jsval v; /* not rooted but ok here because we know GC won't run */
+    jsdouble d = (jsdouble)i;
+    if (!js_NewDoubleInRootedValue(cx, d, &v))
+        return JSVAL_ERROR_COOKIE;
+    return v;
 } 
 
+FASTCALL jsdouble builtin_UnboxDouble(jsval v)
+{
+    if (JS_LIKELY(JSVAL_IS_INT(v)))
+        return (jsdouble)JSVAL_TO_INT(v);
+    return *JSVAL_TO_DOUBLE(v);
+}
+
 FASTCALL jsint builtin_UnboxInt32(jsval v)
 {
     if (JS_LIKELY(JSVAL_IS_INT(v)))
         return JSVAL_TO_INT(v);
     jsint i;
     if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
         return i;
     return INT32_ERROR_COOKIE;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -149,51 +149,30 @@ static struct CallInfo builtins[] = {
 #include "builtins.tbl"
 };
 
 #undef NAME
 #undef BUILTIN1
 #undef BUILTIN2
 #undef BUILTIN3
 
-/* Return the tag of a jsval. Doubles are checked whether they actually
-   represent an int, in which case we treat them as JSVAL_INT. */
-static inline int getType(jsval v)
+/* Return the coerced type of a value. If its a number, we always return JSVAL_DOUBLE, no matter
+   whether its represented as an int or as a double. */
+static inline int getCoercedType(jsval v)
 {
     if (JSVAL_IS_INT(v))
-        return JSVAL_INT;
-    jsint i;
-    if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
-        return JSVAL_INT;
+        return JSVAL_DOUBLE;
     return JSVAL_TAG(v);
 }
 
-static inline bool isInt(jsval v)
-{
-    return getType(v) == JSVAL_INT;
-}
-
-static inline bool isDouble(jsval v)
-{
-    return getType(v) == JSVAL_DOUBLE;
-}
-
 static inline bool isNumber(jsval v)
 {
     return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v);
 }
 
-static inline jsint asInt(jsval v)
-{
-    JS_ASSERT(isInt(v));
-    if (JSVAL_IS_DOUBLE(v))
-        return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v));
-    return JSVAL_TO_INT(v);
-}
-
 static inline jsdouble asNumber(jsval v)
 {
     JS_ASSERT(isNumber(v));
     if (JSVAL_IS_DOUBLE(v))
         return *JSVAL_TO_DOUBLE(v);
     return (jsdouble)JSVAL_TO_INT(v);
 }
 
@@ -288,46 +267,53 @@ public:
         LirWriter(out), recorder(_recorder)
     {
     }
 
     /* Determine the type of a store by looking at the current type of the actual value the
        interpreter is using. For numbers we have to check what kind of store we used last
        (integer or double) to figure out what the side exit show reflect in its typemap. */
     int getStoreType(jsval& v) {
-        if (isNumber(v)) 
-            return recorder.get(&v)->isQuad() ? JSVAL_DOUBLE : JSVAL_INT;
-        return JSVAL_TAG(v);
+        int t = isNumber(v) 
+            ? (recorder.get(&v)->isQuad() ? JSVAL_DOUBLE : JSVAL_INT)
+            : JSVAL_TAG(v);
+#ifdef DEBUG
+         printf("%c", "OID?S?B"[t]);
+#endif         
+         return t;
     }
     
     /* Write out a type map for the current scopes and all outer scopes,
        up until the entry scope. */
     void
     buildTypeMap(JSStackFrame* fp, JSFrameRegs& regs, char* m)
     {
+#ifdef DEBUG
+        printf("side exit type map: ");
+#endif        
         if (fp != recorder.getEntryFrame())
             buildTypeMap(fp->down, *fp->down->regs, m);
         for (unsigned n = 0; n < fp->argc; ++n)
             *m++ = getStoreType(fp->argv[n]);
         for (unsigned n = 0; n < fp->nvars; ++n)
             *m++ = getStoreType(fp->vars[n]);
         for (jsval* sp = fp->spbase; sp < regs.sp; ++sp)
             *m++ = getStoreType(*sp);
+#ifdef DEBUG
+        printf("\n");
+#endif        
     }
 
     virtual LInsp insGuard(LOpcode v, LIns *c, SideExit *x) {
         VMSideExitInfo* i = (VMSideExitInfo*)x->vmprivate;
         buildTypeMap(recorder.getFp(), recorder.getRegs(), i->typeMap);
         return out->insGuard(v, c, x);
     }
 };
 
-static void
-buildTypeMap(JSStackFrame* entryFrame, JSStackFrame* fp, JSFrameRegs& regs, char* m);
-
 TraceRecorder::TraceRecorder(JSContext* cx, Fragmento* fragmento, Fragment* _fragment)
 {
     this->cx = cx;
     this->fragment = _fragment;
     entryFrame = cx->fp;
     entryRegs.pc = entryFrame->regs->pc;
     entryRegs.sp = entryFrame->regs->sp;
 
@@ -349,17 +335,23 @@ TraceRecorder::TraceRecorder(JSContext* 
     lir = func_filter = new (&gc) FuncFilter(lir);
     lir->ins0(LIR_trace);
     /* generate the entry map and stash it in the trace */
     entryNativeFrameSlots = nativeFrameSlots(entryFrame, entryRegs);
     maxNativeFrameSlots = entryNativeFrameSlots;
     LIns* data = lir_buf_writer->skip(sizeof(VMFragmentInfo) + 
             entryNativeFrameSlots * sizeof(char));
     fragmentInfo = (VMFragmentInfo*)data->payload();
-    buildTypeMap(entryFrame, entryFrame, entryRegs, fragmentInfo->typeMap);
+    char* m = fragmentInfo->typeMap;
+    for (unsigned n = 0; n < entryFrame->argc; ++n)
+        *m++ = getCoercedType(entryFrame->argv[n]);
+    for (unsigned n = 0; n < entryFrame->nvars; ++n)
+        *m++ = getCoercedType(entryFrame->vars[n]);
+    for (jsval* sp = entryFrame->spbase; sp < entryRegs.sp; ++sp)
+        *m++ = getCoercedType(*sp);
     fragmentInfo->nativeStackBase = nativeFrameOffset(&cx->fp->spbase[0]);
     fragment->vmprivate = fragmentInfo;
     fragment->param0 = lir->insImm8(LIR_param, Assembler::argRegs[0], 0);
     fragment->param1 = lir->insImm8(LIR_param, Assembler::argRegs[1], 0);
     fragment->sp = lir->insLoadi(fragment->param0, offsetof(InterpState, sp));
     cx_ins = lir->insLoadi(fragment->param0, offsetof(InterpState, cx));
 #ifdef DEBUG
     lirbuf->names->addName(fragment->param0, "state");
@@ -472,108 +464,109 @@ TraceRecorder::nativeFrameOffset(void* p
    execution. */
 void
 TraceRecorder::trackNativeFrameUse(unsigned slots)
 {
     if (slots > maxNativeFrameSlots)
         maxNativeFrameSlots = slots;
 }
 
-/* Write out a type map for the current scopes and all outer scopes,
-   up until the entry scope. */
-static void
-buildTypeMap(JSStackFrame* entryFrame, JSStackFrame* fp, JSFrameRegs& regs, char* m)
-{
-    if (fp != entryFrame)
-        buildTypeMap(entryFrame, fp->down, *fp->down->regs, m);
-    for (unsigned n = 0; n < fp->argc; ++n)
-        *m++ = getType(fp->argv[n]);
-    for (unsigned n = 0; n < fp->nvars; ++n)
-        *m++ = getType(fp->vars[n]);
-    for (jsval* sp = fp->spbase; sp < regs.sp; ++sp)
-        *m++ = getType(*sp);
-}
-
 /* Unbox a jsval into a slot. Slots are wide enough to hold double values
    directly (instead of storing a pointer to them). */
 static bool
 unbox_jsval(jsval v, int t, double* slot)
 {
-    if (t != getType(v))
+    if (JSVAL_IS_INT(v)) {
+        if (t == JSVAL_INT || t == JSVAL_DOUBLE) {
+            JS_ASSERT(t == JSVAL_INT || t == JSVAL_DOUBLE);
+            jsint i = JSVAL_TO_INT(v);
+            if (t == JSVAL_INT)
+                *(jsint*)slot = i;
+            else
+                *(jsdouble*)slot = (jsdouble)i;
+            return true;
+        }
         return false;
-    switch (t) {
-      case JSVAL_BOOLEAN:
+    }
+    if (JSVAL_TAG(v) != (jsuint)t)
+        return false;
+    switch (JSVAL_TAG(v)) {
+    case JSVAL_BOOLEAN:
         *(bool*)slot = JSVAL_TO_BOOLEAN(v);
         break;
-      case JSVAL_INT:
-        *(jsint*)slot = asInt(v);
-        break;
-      case JSVAL_DOUBLE:
+    case JSVAL_DOUBLE:
         *(jsdouble*)slot = *JSVAL_TO_DOUBLE(v);
         break;
-      case JSVAL_STRING:
+    case JSVAL_STRING:
         *(JSString**)slot = JSVAL_TO_STRING(v);
         break;
-      default:
+    default:
         JS_ASSERT(JSVAL_IS_GCTHING(v));
         *(void**)slot = JSVAL_TO_GCTHING(v);
     }
     return true;
 }
 
 /* Box a value from the native stack back into the jsval format. Integers
    that are too large to fit into a jsval are automatically boxed into
    heap-allocated doubles. */
 static bool
 box_jsval(JSContext* cx, jsval* vp, int t, double* slot)
 {
+    jsdouble d;
     switch (t) {
       case JSVAL_BOOLEAN:
         *vp = BOOLEAN_TO_JSVAL(*(bool*)slot);
         break;
       case JSVAL_INT:
         jsint i = *(jsint*)slot;
-        if (INT_FITS_IN_JSVAL(i))
+        if (INT_FITS_IN_JSVAL(i)) {
             *vp = INT_TO_JSVAL(i);
-        else
-            return js_NewDoubleInRootedValue(cx, (jsdouble)i, vp);
-        break;
+            break;
+        }
+        d = (jsdouble)i;
+        goto allocate_double;
       case JSVAL_DOUBLE:
-        return js_NewDoubleInRootedValue(cx, *slot, vp);
+        d = *slot;
+     allocate_double:
+        /* GC is not allowed to hit as we come out of the native frame. We have to teach
+           the GC how to scan native frames to avoid this race condition. */
+        JS_ASSERT(cx->doubleFreeList != NULL);
+        return js_NewDoubleInRootedValue(cx, d, vp);
       case JSVAL_STRING:
         *vp = STRING_TO_JSVAL(*(JSString**)slot);
         break;
       default:
         JS_ASSERT(t == JSVAL_OBJECT);
         *vp = OBJECT_TO_JSVAL(*(JSObject**)slot);
         break;
     }
     return true;
 }
 
-/* Attempt to unbox the given JS frame into a native frame, checking
-   along the way that the supplied typemap holds. */
+/* Attempt to unbox the given JS frame into a native frame, checking along the way that the 
+   supplied typemap holds. */
 static bool
 unbox(JSStackFrame* fp, JSFrameRegs& regs, char* m, double* native)
 {
     jsval* vp;
     for (vp = fp->argv; vp < fp->argv + fp->argc; ++vp)
         if (!unbox_jsval(*vp, (JSType)*m++, native++))
             return false;
     for (vp = fp->vars; vp < fp->vars + fp->nvars; ++vp)
         if (!unbox_jsval(*vp, (JSType)*m++, native++))
             return false;
     for (vp = fp->spbase; vp < regs.sp; ++vp)
         if (!unbox_jsval(*vp, (JSType)*m++, native++))
             return false;
     return true;
 }
 
-/* Attempt to unbox the given JS frame into a native frame, checking
-   along the way that the supplied typemap holds. */
+/* Box the given native frame into a JS frame. This only fails due to a hard error 
+   (out of memory for example). */
 static bool
 box(JSContext* cx, JSStackFrame* fp, JSFrameRegs& regs, char* m, double* native)
 {
     jsval* vp;
     for (vp = fp->argv; vp < fp->argv + fp->argc; ++vp)
         if (!box_jsval(cx, vp, (JSType)*m++, native++))
             return false;
     for (vp = fp->vars; vp < fp->vars + fp->nvars; ++vp)
@@ -585,28 +578,27 @@ box(JSContext* cx, JSStackFrame* fp, JSF
     return true;
 }
 
 /* Emit load instructions onto the trace that read the initial stack state. */
 void
 TraceRecorder::import(jsval* p, char *prefix, int index)
 {
     JS_ASSERT(onFrame(p));
-    LIns *ins = lir->insLoad(isDouble(*p) ? LIR_ldq : LIR_ld,
+    LIns *ins = lir->insLoad(isNumber(*p) ? LIR_ldq : LIR_ld,
             fragment->sp, -fragmentInfo->nativeStackBase + nativeFrameOffset(p) + 8);
     tracker.set(p, ins);
 #ifdef DEBUG
     if (prefix) {
         char name[16];
         JS_ASSERT(strlen(prefix) < 10);
         JS_snprintf(name, sizeof name, "$%s%d", prefix, index);
         lirbuf->names->addName(ins, name);
     }
 #endif
-
 }
 
 /* Update the tracker. If the value is part of any argv/vars/stack of any
    currently active frame (onFrame), then issue a write back store. */
 void
 TraceRecorder::set(void* p, LIns* i)
 {
     tracker.set(p, i);
@@ -662,48 +654,38 @@ TraceRecorder::snapshot()
 void
 TraceRecorder::guard(bool expected, LIns* cond)
 {
     lir->insGuard(expected ? LIR_xf : LIR_xt,
                   cond,
                   snapshot());
 }
 
-/* See if the type of a loop variable matches its loop entry type, or whether we can at least
-   make it match by promoting it. */
 bool
-TraceRecorder::adjustType(jsval& v, int type)
+TraceRecorder::checkType(jsval& v, int type)
 {
-    /* if the type is still the same, we are done */
-    if (getType(v) == type)
+    if (type == JSVAL_DOUBLE && isNumber(v))
         return true;
-    printf("getType(v): %d type: %d\n", getType(v), type);
-    /* if its an integer now, but we want a double at entry, make it so */
-    if (getType(v) == JSVAL_INT && type == JSVAL_DOUBLE) {
-        set(&v, lir->ins1(LIR_i2f, get(&v)));
-        return true;
-    }
-    /* fail, incompatible types */
-    return false;
+    return JSVAL_TAG(v) == (jsuint)type;
 }
 
 /* Make sure that all loop-carrying values have a stable type along the loop edge. */
 bool
 TraceRecorder::verifyTypeStability(JSStackFrame* fp, JSFrameRegs& regs, char* m)
 {
     if (fp != entryFrame)
         verifyTypeStability(fp->down, *fp->down->regs, m);
     for (unsigned n = 0; n < fp->argc; ++n, ++m)
-        if (!adjustType(fp->argv[n], *m))
+        if (!checkType(fp->argv[n], *m))
             return false;
     for (unsigned n = 0; n < fp->nvars; ++n, ++m)
-        if (!adjustType(fp->vars[n], *m))
+        if (!checkType(fp->vars[n], *m))
             return false;
     for (jsval* sp = fp->spbase; sp < regs.sp; ++sp, ++m)
-        if (!adjustType(*sp, *m))
+        if (!checkType(*sp, *m))
             return false;
     return true;
 }
 
 void
 TraceRecorder::closeLoop(Fragmento* fragmento)
 {
     if (!verifyTypeStability(entryFrame, entryRegs, fragmentInfo->typeMap)) {
@@ -776,17 +758,22 @@ js_LoopEdge(JSContext* cx)
     }
 
     /* execute previously recorded race */
     VMFragmentInfo* fi = (VMFragmentInfo*)f->vmprivate;
     double native[fi->maxNativeFrameSlots+1];
 #ifdef DEBUG
     *(uint64*)&native[fi->maxNativeFrameSlots] = 0xdeadbeefdeadbeefLL;
 #endif
-    unbox(cx->fp, *cx->fp->regs, fi->typeMap, native);
+    if (!unbox(cx->fp, *cx->fp->regs, fi->typeMap, native)) {
+#ifdef DEBUG
+        printf("typemap mismatch, skipping trace.\n");
+#endif        
+        return false;
+    }
     double* entry_sp = &native[fi->nativeStackBase/sizeof(double) + 
                                (cx->fp->regs->sp - cx->fp->spbase - 1)];
     state.sp = (void*)entry_sp;
     state.rp = NULL;
     state.f = NULL;
     state.cx = cx;
     union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
     u.code = f->code();
@@ -886,16 +873,21 @@ TraceRecorder::stack(int n)
 }
 
 void
 TraceRecorder::stack(int n, LIns* i)
 {
     set(&stackval(n), i);
 }
 
+LIns* TraceRecorder::f2i(LIns* f)
+{
+    return lir->insCall(F_doubleToInt32, &f);
+}
+
 bool TraceRecorder::ifop(bool sense)
 {
     jsval& v = stackval(-1);
     LIns* cond_ins;
     bool cond;
     if (JSVAL_IS_BOOLEAN(v)) {
         cond_ins = lir->ins_eq0(jsval_to_boolean(get(&v)));
         cond = JSVAL_TO_BOOLEAN(v);
@@ -909,53 +901,53 @@ bool TraceRecorder::ifop(bool sense)
     }
     guard(cond, cond_ins);
     return true;
 }
 
 bool
 TraceRecorder::inc(jsval& v, jsint incr, bool pre)
 {
-    if (isInt(v)) {
+    if (isNumber(v)) {
+        jsdouble d = (jsdouble)incr;
         LIns* before = get(&v);
-        LIns* after = lir->ins2i(LIR_add, before, incr);
-        guard(false, lir->ins1(LIR_ov, after));
+        LIns* after = lir->ins2(LIR_fadd, before, lir->insImmq(*(uint64_t*)&d));
         set(&v, after);
         stack(0, pre ? after : before);
         return true;
     }
     return false;
 }
 
 bool
 TraceRecorder::cmp(LOpcode op, bool negate)
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
-    if (isInt(l) && isInt(r)) {
+    if (isNumber(l) && isNumber(r)) {
         LIns* x = lir->ins2(op, get(&l), get(&r));
         if (negate)
-            x = lir->ins2i(LIR_eq, x, 0);
+            x = lir->ins_eq0(x);
         bool cond;
         switch (op) {
-          case LIR_lt:
-            cond = asInt(l) < asInt(r);
+          case LIR_flt:
+            cond = asNumber(l) < asNumber(r);
             break;
-          case LIR_gt:
-            cond = asInt(l) > asInt(r);
+          case LIR_fgt:
+            cond = asNumber(l) > asNumber(r);
             break;
-          case LIR_le:
-            cond = asInt(l) <= asInt(r);
+          case LIR_fle:
+            cond = asNumber(l) <= asNumber(r);
             break;
-          case LIR_ge:
-            cond = asInt(l) >= asInt(r);
+          case LIR_fge:
+            cond = asNumber(l) >= asNumber(r);
             break;
           default:
-            JS_ASSERT(cond == LIR_eq);
-            cond = asInt(l) == asInt(r);
+            JS_ASSERT(cond == LIR_feq);
+            cond = asNumber(l) == asNumber(r);
             break;
         }
         /* The interpreter fuses comparisons and the following branch,
            so we have to do that here as well. */
         if (cx->fp->regs->pc[1] == ::JSOP_IFEQ)
             guard(!cond, x);
         else if (cx->fp->regs->pc[1] == ::JSOP_IFNE)
             guard(cond, x);
@@ -973,17 +965,17 @@ TraceRecorder::cmp(LOpcode op, bool nega
 bool
 TraceRecorder::unary(LOpcode op)
 {
     jsval& v = stackval(-1);
     bool intop = !(op & LIR64);
     if (isNumber(v)) {
         LIns* a = get(&v);
         if (intop)
-            a = lir->insCall(F_doubleToInt32, &a);
+            a = f2i(a);
         a = lir->ins1(op, a);
         if (intop)
             a = lir->ins1(LIR_i2f, a);
         set(&v, a);
         return true;
     }
     return false;
 }
@@ -994,51 +986,28 @@ TraceRecorder::binary(LOpcode op)
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
     bool intop = !(op & LIR64);
     if (isNumber(l) && isNumber(r)) {
         LIns* a = get(&l);
         LIns* b = get(&r);
         if (intop) {
             a = lir->insCall(op == LIR_ush ? F_doubleToUint32 : F_doubleToInt32, &a);
-            b = lir->insCall(F_doubleToInt32, &b);
+            b = f2i(b);
         }
         a = lir->ins2(op, a, b);
         if (intop)
             a = lir->ins1(op == LIR_ush ? LIR_u2f : LIR_i2f, a);
         set(&l, a);
         return true;
     }
     return false;
 }
 
 bool
-TraceRecorder::iunary(LOpcode op)
-{
-    jsval& v = stackval(-1);
-    if (isNumber(v)) {
-        set(&v, lir->ins1(op, get(&v)));
-        return true;
-    }
-    return false;
-}
-
-bool
-TraceRecorder::ibinary(LOpcode op)
-{
-    jsval& r = stackval(-1);
-    jsval& l = stackval(-2);
-    if (isInt(l) && isInt(r)) {
-        set(&l, lir->ins2(op, get(&l), get(&r)));
-        return true;
-    }
-    return false;
-}
-
-bool
 TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins)
 {
     LIns* ops = lir->insLoadi(map_ins, offsetof(JSObjectMap, ops));
     if (map->ops == &js_ObjectOps) {
         guard(true, lir->ins2(LIR_eq, ops, lir->insImmPtr(&js_ObjectOps)));
         return true;
     }
     LIns* n = lir->insLoadi(ops, offsetof(JSObjectOps, newObjectMap));
@@ -1197,33 +1166,33 @@ TraceRecorder::jsval_to_object(LIns* v_i
 {
     guard_jsval_tag(v_ins, JSVAL_OBJECT);
     return lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)~JSVAL_TAGMASK));
 }
 
 bool
 TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
 {
-    if (isInt(v))
+    if (JSVAL_IS_INT(v))
         v_ins = int32_to_jsval(v_ins);
-    else if (isDouble(v))
+    else if (JSVAL_IS_DOUBLE(v))
         v_ins = double_to_jsval(v_ins);
     else if (JSVAL_IS_BOOLEAN(v))
         v_ins = boolean_to_jsval(v_ins);
     else
         return false; /* don't know how to box this type */
     return true;
 }
 
 bool
 TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
 {
-    if (isInt(v))
+    if (JSVAL_IS_INT(v))
         v_ins = jsval_to_int32(v_ins);
-    else if (isDouble(v))
+    else if (JSVAL_IS_DOUBLE(v))
         v_ins = jsval_to_double(v_ins);
     else if (JSVAL_IS_BOOLEAN(v))
         v_ins = jsval_to_boolean(v_ins);
     else
         return false; /* we don't know how to convert that type */
     return true;
 }
 
@@ -1317,94 +1286,94 @@ bool TraceRecorder::JSOP_DUP2()
     return true;
 }
 bool TraceRecorder::JSOP_SETCONST()
 {
     return false;
 }
 bool TraceRecorder::JSOP_BITOR()
 {
-    return ibinary(LIR_or);
+    return binary(LIR_or);
 }
 bool TraceRecorder::JSOP_BITXOR()
 {
-    return ibinary(LIR_xor);
+    return binary(LIR_xor);
 }
 bool TraceRecorder::JSOP_BITAND()
 {
-    return ibinary(LIR_and);
+    return binary(LIR_and);
 }
 bool TraceRecorder::JSOP_EQ()
 {
-    return cmp(LIR_eq);
+    return cmp(LIR_feq);
 }
 bool TraceRecorder::JSOP_NE()
 {
-    return cmp(LIR_eq, true);
+    return cmp(LIR_feq, true);
 }
 bool TraceRecorder::JSOP_LT()
 {
-    return cmp(LIR_lt);
+    return cmp(LIR_flt);
 }
 bool TraceRecorder::JSOP_LE()
 {
-    return cmp(LIR_le);
+    return cmp(LIR_fle);
 }
 bool TraceRecorder::JSOP_GT()
 {
-    return cmp(LIR_gt);
+    return cmp(LIR_fgt);
 }
 bool TraceRecorder::JSOP_GE()
 {
-    return cmp(LIR_ge);
+    return cmp(LIR_fge);
 }
 bool TraceRecorder::JSOP_LSH()
 {
-    return ibinary(LIR_lsh);
+    return binary(LIR_lsh);
 }
 bool TraceRecorder::JSOP_RSH()
 {
-    return ibinary(LIR_rsh);
+    return binary(LIR_rsh);
 }
 bool TraceRecorder::JSOP_URSH()
 {
-    return ibinary(LIR_ush);
+    return binary(LIR_ush);
 }
 bool TraceRecorder::JSOP_ADD()
 {
-    return false;
+    return binary(LIR_fadd);
 }
 bool TraceRecorder::JSOP_SUB()
 {
-    return false;
+    return binary(LIR_fsub);
 }
 bool TraceRecorder::JSOP_MUL()
 {
-    return false;
+    return binary(LIR_fmul);
 }
 bool TraceRecorder::JSOP_DIV()
 {
-    return false;
+    return binary(LIR_fdiv);
 }
 bool TraceRecorder::JSOP_MOD()
 {
     return false;
 }
 bool TraceRecorder::JSOP_NOT()
 {
     jsval& v = stackval(-1);
     if (JSVAL_IS_BOOLEAN(v)) {
         set(&v, lir->ins_eq0(get(&v)));
         return true;
     }
     return false;
 }
 bool TraceRecorder::JSOP_BITNOT()
 {
-    return iunary(LIR_not);
+    return unary(LIR_not);
 }
 bool TraceRecorder::JSOP_NEG()
 {
     return false;
 }
 bool TraceRecorder::JSOP_NEW()
 {
     return false;
@@ -1485,27 +1454,27 @@ bool TraceRecorder::JSOP_SETPROP()
 {
     return false;
 }
 bool TraceRecorder::JSOP_GETELEM()
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
     /* no guards for type checks, trace specialized this already */
-    if (!isInt(r) || JSVAL_IS_PRIMITIVE(l))
+    if (!JSVAL_IS_INT(r) || JSVAL_IS_PRIMITIVE(l))
         return false;
     JSObject* obj = JSVAL_TO_OBJECT(l);
     LIns* obj_ins = get(&l);
     /* make sure the object is actually a dense array */
     LIns* dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
     if (!guardThatObjectIsDenseArray(obj, obj_ins, dslots_ins))
         return false;
     /* check that the index is within bounds */
-    jsint idx = asInt(r);
-    LIns* idx_ins = get(&r);
+    jsint idx = JSVAL_TO_INT(r);
+    LIns* idx_ins = f2i(get(&r));
     if (!guardDenseArrayIndexWithinBounds(obj, idx, obj_ins, dslots_ins, idx_ins))
         return false;
     jsval v = obj->dslots[idx];
     /* ok, we can trace this case since we now have the value and thus know the type */
     LIns* addr = lir->ins2(LIR_add, dslots_ins, 
             lir->ins2i(LIR_lsh, idx_ins, sizeof(jsval) == 4 ? 2 : 3));
     /* load the value, check the type (need to check JSVAL_HOLE only for booleans) */
     LIns* v_ins = lir->insLoadi(addr, 0);
@@ -1515,27 +1484,27 @@ bool TraceRecorder::JSOP_GETELEM()
     return true;
 }
 bool TraceRecorder::JSOP_SETELEM()
 {
     jsval& v = stackval(-1);
     jsval& r = stackval(-2);
     jsval& l = stackval(-3);
     /* no guards for type checks, trace specialized this already */
-    if (!isInt(r) || JSVAL_IS_PRIMITIVE(l))
+    if (!JSVAL_IS_INT(r) || JSVAL_IS_PRIMITIVE(l))
         return false;
     JSObject* obj = JSVAL_TO_OBJECT(l);
     LIns* obj_ins = get(&l);
     /* make sure the object is actually a dense array */
     LIns* dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
     if (!guardThatObjectIsDenseArray(obj, obj_ins, dslots_ins))
         return false;
     /* check that the index is within bounds */
-    jsint idx = asInt(r);
-    LIns* idx_ins = get(&r);
+    jsint idx = JSVAL_TO_INT(r);
+    LIns* idx_ins = f2i(get(&r));
     if (!guardDenseArrayIndexWithinBounds(obj, idx, obj_ins, dslots_ins, idx_ins))
         return false;
     /* get us the address of the array slot */
     LIns* addr = lir->ins2(LIR_add, dslots_ins, 
                            lir->ins2i(LIR_lsh, idx_ins, JS_BYTES_PER_WORD_LOG2));
     LIns* oldval = lir->insLoad(LIR_ld, addr, 0);
     LIns* isHole = lir->ins2(LIR_eq, oldval, lir->insImmPtr((void*)JSVAL_HOLE));
     LIns* count = lir->insLoadi(obj_ins,
@@ -1581,34 +1550,33 @@ bool TraceRecorder::JSOP_NAME()
         return false;
 
     stack(0, v_ins);
     return true;
 }
 bool TraceRecorder::JSOP_DOUBLE()
 {
     jsval v = (jsval)cx->fp->script->atomMap.vector[GET_INDEX(cx->fp->regs->pc)];
-    if (isInt(v))
-        stack(0, lir->insImm(asInt(v)));
-    else
-        stack(0, lir->insImmq(*(uint64_t*)JSVAL_TO_DOUBLE(v)));
+    stack(0, lir->insImmq(*(uint64_t*)JSVAL_TO_DOUBLE(v)));
     return true;
 }
 bool TraceRecorder::JSOP_STRING()
 {
     return false;
 }
 bool TraceRecorder::JSOP_ZERO()
 {
-    stack(0, lir->insImm(0));
+    jsdouble d = (jsdouble)0;
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_ONE()
 {
-    stack(0, lir->insImm(1));
+    jsdouble d = (jsdouble)1;
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_NULL()
 {
     stack(0, lir->insImmPtr(NULL));
     return true;
 }
 bool TraceRecorder::JSOP_THIS()
@@ -1622,34 +1590,20 @@ bool TraceRecorder::JSOP_FALSE()
 }
 bool TraceRecorder::JSOP_TRUE()
 {
     stack(0, lir->insImm(1));
     return true;
 }
 bool TraceRecorder::JSOP_OR()
 {
-    jsval& r = stackval(-1);
-    jsval& l = stackval(-2);
-    if (JSVAL_IS_BOOLEAN(l) && JSVAL_IS_BOOLEAN(r)) {
-        LIns* result = lir->ins2(LIR_or, get(&l), get(&r));
-        set(&l, result);
-        return true;
-    }
     return false;
 }
 bool TraceRecorder::JSOP_AND()
 {
-    jsval& r = stackval(-1);
-    jsval& l = stackval(-2);
-    if (JSVAL_IS_BOOLEAN(l) && JSVAL_IS_BOOLEAN(r)) {
-        LIns* result = lir->ins2(LIR_and, get(&l), get(&r));
-        set(&l, result);
-        return true;
-    }
     return false;
 }
 bool TraceRecorder::JSOP_TABLESWITCH()
 {
     return false;
 }
 bool TraceRecorder::JSOP_LOOKUPSWITCH()
 {
@@ -1720,17 +1674,18 @@ bool TraceRecorder::JSOP_GETVAR()
 }
 bool TraceRecorder::JSOP_SETVAR()
 {
     var(GET_VARNO(cx->fp->regs->pc), stack(-1));
     return true;
 }
 bool TraceRecorder::JSOP_UINT16()
 {
-    stack(0, lir->insImm((jsint) GET_UINT16(cx->fp->regs->pc)));
+    jsdouble d = (jsdouble)GET_UINT16(cx->fp->regs->pc);
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_NEWINIT()
 {
     return false;
 }
 bool TraceRecorder::JSOP_ENDINIT()
 {
@@ -2157,17 +2112,18 @@ bool TraceRecorder::JSOP_UNUSED186()
     return false;
 }
 bool TraceRecorder::JSOP_DELDESC()
 {
     return false;
 }
 bool TraceRecorder::JSOP_UINT24()
 {
-    stack(0, lir->insImm((jsint) GET_UINT24(cx->fp->regs->pc)));
+    jsdouble d = (jsdouble) GET_UINT24(cx->fp->regs->pc);
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_INDEXBASE()
 {
     return false;
 }
 bool TraceRecorder::JSOP_RESETBASE()
 {
@@ -2314,22 +2270,24 @@ bool TraceRecorder::JSOP_CALLARG()
     return false;
 }
 bool TraceRecorder::JSOP_CALLLOCAL()
 {
     return false;
 }
 bool TraceRecorder::JSOP_INT8()
 {
-    stack(0, lir->insImm(GET_INT8(cx->fp->regs->pc)));
+    jsdouble d = (jsdouble)GET_INT8(cx->fp->regs->pc);
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_INT32()
 {
-    stack(0, lir->insImm(GET_INT32(cx->fp->regs->pc)));
+    jsdouble d = (jsdouble)GET_INT32(cx->fp->regs->pc);
+    stack(0, lir->insImmq(*(uint64_t*)&d));
     return true;
 }
 bool TraceRecorder::JSOP_LENGTH()
 {
     return false;
 }
 bool TraceRecorder::JSOP_NEWARRAY()
 {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -122,38 +122,38 @@ class TraceRecorder {
     JSStackFrame* findFrame(void* p) const;
     bool onFrame(void* p) const;
     unsigned nativeFrameSlots(JSStackFrame* fp, JSFrameRegs& regs) const;
     size_t   nativeFrameOffset(void* p) const;
     void import(jsval*, char *prefix = NULL, int index = 0);
     void trackNativeFrameUse(unsigned slots);
     
     nanojit::SideExit* snapshot();
-
     unsigned getCallDepth() const;
+    void guard(bool expected, nanojit::LIns* cond);
 
     void set(void* p, nanojit::LIns* l);
 
-    void guard(bool expected, nanojit::LIns* cond);
-
-    bool adjustType(jsval& v, int type);
+    bool checkType(jsval& v, int type);
     bool verifyTypeStability(JSStackFrame* fp, JSFrameRegs& regs, char* m);
     void closeLoop(nanojit::Fragmento* fragmento);
     
     jsval& argval(unsigned n) const;
     jsval& varval(unsigned n) const;
     jsval& stackval(int n) const;
     
     nanojit::LIns* arg(unsigned n);
     void arg(unsigned n, nanojit::LIns* i);
     nanojit::LIns* var(unsigned n);
     void var(unsigned n, nanojit::LIns* i);
     nanojit::LIns* stack(int n);
     void stack(int n, nanojit::LIns* i);
     
+    nanojit::LIns* f2i(nanojit::LIns* f);
+
     bool ifop(bool sense);
     bool inc(jsval& v, jsint incr, bool pre);
     bool cmp(nanojit::LOpcode op, bool negate = false);
 
     bool unary(nanojit::LOpcode op);
     bool binary(nanojit::LOpcode op);
     
     bool ibinary(nanojit::LOpcode op); 
@@ -433,21 +433,22 @@ public:
     bool JSOP_INT8();
     bool JSOP_INT32();
     bool JSOP_LENGTH();
     bool JSOP_NEWARRAY();
     bool JSOP_HOLE();
 };
 
 FASTCALL jsdouble builtin_dmod(jsdouble a, jsdouble b);
-FASTCALL jsval builtin_BoxDouble(JSContext* cx, jsdouble d);
-FASTCALL jsval builtin_BoxInt32(JSContext* cx, jsint i);
-FASTCALL jsint builtin_UnboxInt32(jsval v);
-FASTCALL int32 builtin_doubleToInt32(jsdouble d);
-FASTCALL int32 builtin_doubleToUint32(jsdouble d);
+FASTCALL jsval    builtin_BoxDouble(JSContext* cx, jsdouble d);
+FASTCALL jsval    builtin_BoxInt32(JSContext* cx, jsint i);
+FASTCALL jsdouble builtin_UnboxDouble(jsval v);
+FASTCALL jsint    builtin_UnboxInt32(jsval v);
+FASTCALL int32    builtin_doubleToInt32(jsdouble d);
+FASTCALL int32    builtin_doubleToUint32(jsdouble d);
 
 /*
  * Trace monitor. Every runtime is associated with a trace monitor that keeps
  * track of loop frequencies for all JavaScript code loaded into that runtime.
  * For this we use a loop table. Adjacent slots in the loop table, one for each
  * loop header in a given script, are requested using lock-free synchronization
  * from the runtime-wide loop table slot space, when the script is compiled.
  *
--- a/js/src/trace.js
+++ b/js/src/trace.js
@@ -1,13 +1,13 @@
 f = function() {
 	var q = 1;
 	//for (var j = 0; j < 500; ++j)
 	for (var i = 0; i < 5000; ++i)
-		//q += 2.5;
-		++q;
+		q += 2.5;
+		//++q;
 	print("q=" + q + " i=" + i);
 }
 
 var before = Date.now();
 f();
 var after = Date.now();
 print(after - before);