Add support to demote stores of constants that are currently represented as float but are really integers. bitwise.js is now compiled complete fp-casts free.
authorAndreas Gal <gal@mozilla.com>
Mon, 07 Jul 2008 01:05:53 -0700
changeset 17482 d88a5e56ec9382799f89b9a87d8b4f9a561ff1af
parent 17481 0f7c4afcd5c83af511ec58c8798e0a4716524f96
child 17483 1aa7e11366a29c3fe26fed7639e140ead5ab5902
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
Add support to demote stores of constants that are currently represented as float but are really integers. bitwise.js is now compiled complete fp-casts free.
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -350,44 +350,28 @@ public:
     }
 
     virtual LInsp insGuard(LOpcode v, LIns *c, SideExit *x) {
         VMSideExitInfo* i = (VMSideExitInfo*)x->vmprivate;
         buildExitMap(recorder.getFp(), recorder.getRegs(), i->typeMap);
         return out->insGuard(v, c, x);
     }
 
-    LIns* seeThroughCastChain(LIns* value) {
-        do {
-            if (value->isop(LIR_i2f)) {
-                value = value->oprnd1();
-                JS_ASSERT(!value->isQuad());
-                continue;
-             }
-            if (value->isCall() && value->imm8() == F_doubleToInt32) {
-                value = callArgN(value, 1);
-                JS_ASSERT(value->isQuad());
-                continue;
-            }
-        } while (false);
-        return value;
-    }
-    
     /* Sink all type casts into the stack into the side exit by simply storing the original
        (uncasted) value. Each guard generates the side exit map based on the types of the
        last stores to every stack location, so its safe to not perform them on-trace. */
     virtual LInsp insStore(LIns* value, LIns* base, LIns* disp) {
-        if (base == recorder.getFragment()->sp)
-            value = seeThroughCastChain(value);
+        if (base == recorder.getFragment()->sp && isPromote(value))
+            value = demote(out, value);
         return out->insStore(value, base, disp);
     }
     
     virtual LInsp insStorei(LIns* value, LIns* base, int32_t d) {
-        if (base == recorder.getFragment()->sp)
-            value = seeThroughCastChain(value);
+        if (base == recorder.getFragment()->sp && isPromote(value))
+            value = demote(out, value);
         return out->insStorei(value, base, d);
     }
 };
 
 TraceRecorder::TraceRecorder(JSContext* cx, Fragmento* fragmento, Fragment* _fragment)
 {
     this->cx = cx;
     this->fragment = _fragment;
@@ -556,37 +540,44 @@ TraceRecorder::trackNativeFrameUse(unsig
         fragmentInfo->maxNativeFrameSlots = slots;
 }
 
 /* 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 (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;
+    jsuint type = TYPEMAP_GET_TYPE(t);
+    if (type == JSVAL_INT) {
+        jsint i;
+        if (JSVAL_IS_INT(v))
+            *(jsint*)slot = JSVAL_TO_INT(v);
+        else if (JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
+            *(jsint*)slot = i;
+        else
+            return false;
+        return true;
     }
-    if (JSVAL_TAG(v) != (jsuint)t)
+    if (type == JSVAL_DOUBLE) {
+        jsdouble d;
+        if (JSVAL_IS_INT(v))
+            d = JSVAL_TO_INT(v);
+        else if (JSVAL_IS_DOUBLE(v))
+            d = *JSVAL_TO_DOUBLE(v);
+        else
+            return false;
+        *(jsdouble*)slot = d;
+        return true;
+    }        
+    if (JSVAL_TAG(v) != type)
         return false;
     switch (JSVAL_TAG(v)) {
     case JSVAL_BOOLEAN:
         *(bool*)slot = JSVAL_TO_BOOLEAN(v);
         break;
-    case JSVAL_DOUBLE:
-        *(jsdouble*)slot = *JSVAL_TO_DOUBLE(v);
-        break;
     case JSVAL_STRING:
         *(JSString**)slot = JSVAL_TO_STRING(v);
         break;
     default:
         JS_ASSERT(JSVAL_IS_GCTHING(v));
         *(void**)slot = JSVAL_TO_GCTHING(v);
     }
     return true;
@@ -671,21 +662,18 @@ TraceRecorder::import(jsval* p, uint8& t
 {
     JS_ASSERT(onFrame(p));
     LIns* ins;
     /* Calculate the offset of this slot relative to the entry stack-pointer value of the
        native stack. Arguments and locals are to the left of the stack pointer (offset
        less than 0). Stack cells start at offset 0. Ed defined the semantics of the stack,
        not me, so don't blame the messenger. */
     size_t offset = -fragmentInfo->nativeStackBase + nativeFrameOffset(p) + 8;
-    /* Check whether we are supposed to demote this slot if its a number (indicated by
-       JSVAL_DOUBLE). */
-    if (TYPEMAP_GET_TYPE(t) == JSVAL_DOUBLE &&
-            !TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DONT_DEMOTE) &&
-            TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DEMOTE)) {
+    if (TYPEMAP_GET_TYPE(t) == JSVAL_INT) { /* demoted */
+        JS_ASSERT(isInt32(*p));
         /* Ok, we have a valid demotion attempt pending, so insert an integer
            read and promote it to double since all arithmetic operations expect
            to see doubles on entry. The first op to use this slot will emit a 
            f2i cast which will cancel out the i2f we insert here. */
         ins = lir->ins1(LIR_i2f, lir->insLoadi(fragment->sp, offset));
     } else {
         JS_ASSERT(isNumber(*p) == (TYPEMAP_GET_TYPE(t) == JSVAL_DOUBLE));
         ins = lir->insLoad(t == JSVAL_DOUBLE ? LIR_ldq : LIR_ld, fragment->sp, offset);
@@ -773,54 +761,55 @@ TraceRecorder::guard(bool expected, LIns
 bool
 TraceRecorder::checkType(jsval& v, uint8& t)
 {
     if (isNumber(v)) {
         /* Initially we start out all numbers as JSVAL_DOUBLE in the type map. If we still
            see a number in v, its a valid trace but we might want to ask to demote the 
            slot if we know or suspect that its integer. */
         LIns* i = get(&v);
-        if (TYPEMAP_GET_TYPE(t) == JSVAL_DOUBLE && 
-                !TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DEMOTE)) {
+        if (TYPEMAP_GET_TYPE(t) == JSVAL_DOUBLE) {
             if (isInt32(v)) { /* value the interpreter calculated should be integer */
                 /* If the value associated with v via the tracker comes from a i2f operation,
                    we can be sure it will always be an int. If we see INCVAR, we similarly
                    speculate that the result will be int, even though this is not
                    guaranteed and this might cause the entry map to mismatch and thus
                    the trace never to be entered. */
                 if (i->isop(LIR_i2f) || 
                         (i->isop(LIR_fadd) && i->oprnd2()->isconstq() && 
                                 fabs(i->oprnd2()->constvalf()) == 1.0)) {
 #ifdef DEBUG
                     printf("demoting type of an entry slot #%d, triggering re-compilation\n",
                             nativeFrameOffset(&v));
 #endif                    
                     JS_ASSERT(!TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DEMOTE) ||
                             TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DONT_DEMOTE));
                     TYPEMAP_SET_FLAG(t, TYPEMAP_FLAG_DEMOTE);
+                    TYPEMAP_SET_TYPE(t, JSVAL_INT);
                     recompileFlag = true;
                     return true; /* keep going */
                 }
             }
             return true; 
         } 
         /* Looks like we are compiling an integer slot. The recorder always casts to doubles
            after each integer operation, or emits an operation that produces a double right
            away. If we started with an integer, we must arrive here pointing at a i2f cast.
            If not, than demoting the slot didn't work out. Flag the slot to be not
            demoted again. */
-        JS_ASSERT(TYPEMAP_GET_TYPE(t) == JSVAL_DOUBLE &&
+        JS_ASSERT(TYPEMAP_GET_TYPE(t) == JSVAL_INT &&
                 TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DEMOTE) &&
                 !TYPEMAP_GET_FLAG(t, TYPEMAP_FLAG_DONT_DEMOTE));
         if (!i->isop(LIR_i2f)) {
 #ifdef DEBUG            
             printf("demoting type of a slot #%d failed, locking it and re-compiling\n",
                     nativeFrameOffset(&v));
 #endif
             TYPEMAP_SET_FLAG(t, TYPEMAP_FLAG_DONT_DEMOTE);
+            TYPEMAP_SET_TYPE(t, JSVAL_DOUBLE);
             recompileFlag = true;
             return true; /* keep going, recompileFlag will trigger error when we are done with
                             all the slots */
             
         }
         JS_ASSERT(isInt32(v));
         /* Looks like we got the final LIR_i2f as we expected. Overwrite the value in that
            slot with the argument of i2f since we want the integer store to flow along
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -95,16 +95,17 @@ struct VMFragmentInfo {
     uint8                   typeMap[0];
 };
 
 struct VMSideExitInfo {
     uint8                   typeMap[0];
 };
 
 #define TYPEMAP_GET_TYPE(x)         ((x) & JSVAL_TAGMASK)
+#define TYPEMAP_SET_TYPE(x, t)      (x = (x & 0xf0) | t)
 #define TYPEMAP_GET_FLAG(x, flag)   ((x) & flag)
 #define TYPEMAP_SET_FLAG(x, flag)   do { (x) |= flag; } while (0)
 
 #define TYPEMAP_FLAG_DEMOTE 0x10 /* try to record as int */
 #define TYPEMAP_FLAG_DONT_DEMOTE 0x20 /* do not try to record as int */
 
 class TraceRecorder {
     JSContext*              cx;