Merge unboxing code into GETELEM code.
authorAndreas Gal <gal@mozilla.com>
Sat, 05 Jul 2008 13:44:48 -0700
changeset 17429 977464d95d02128f8a5071cb41c4d5518ea48e41
parent 17428 788d1cebba153024a3dd5f932495339fa6d888ae
child 17430 c0ee4caf332f33b3e992a3e7087b2e874edee87c
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.1a1pre
Merge unboxing code into GETELEM code.
js/src/builtins.tbl
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/builtins.tbl
+++ b/js/src/builtins.tbl
@@ -33,10 +33,10 @@
  * 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,  Q,      uint64,    JSContext*, jsdouble,               1, 1)
 BUILTIN2(BoxInt32,              LO, LO, Q,      uint64,    JSContext*, jsint,                  1, 1)
-BUILTIN1(UnboxInt32,            LO, LO,         jsint,     jsval*,                             1, 1)
+BUILTIN1(UnboxInt32,            LO,     Q,      uint64,    jsval*,                             1, 1)
 BUILTIN2(dmod,                  F,  F,  F,      jsdouble,  jsdouble, jsdouble,                 1, 1)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -176,22 +176,24 @@ inline uint64 builtin_BoxDouble(JSContex
 
 inline uint64 builtin_BoxInt32(JSContext* cx, jsint i)
 {
     if (INT_FITS_IN_JSVAL(i)) 
         return INT_TO_JSVAL(i) & 0xffffffffLL;
     return builtin_BoxDouble(cx, (jsdouble)i);
 }
 
-inline jsint builtin_UnboxInt32(JSContext* cx, jsval v)
+inline uint64 builtin_UnboxInt32(JSContext* cx, jsval v)
 {
     if (JSVAL_IS_INT(v))
         return JSVAL_TO_INT(v);
-    JS_ASSERT(JSVAL_IS_DOUBLE(v));
-    return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v));
+    jsint i;
+    if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
+        return i;
+    return 1LL << 32;
 }
 
 #define BUILTIN1(op, at0, atr, tr, t0, cse, fold) \
     { (intptr_t)&builtin_##op, (at0 << 2) | atr, cse, fold NAME(op) },
 #define BUILTIN2(op, at0, at1, atr, tr, t0, t1, cse, fold) \
     { (intptr_t)&builtin_##op, (at0 << 4) | (at1 << 2) | atr, cse, fold NAME(op) },
 #define BUILTIN3(op, at0, at1, at2, atr, tr, t0, t1, t2, cse, fold) \
     { (intptr_t)&builtin_##op, (at0 << 6) | (at1 << 4) | (at2 << 2) | atr, cse, fold NAME(op) },
@@ -818,94 +820,151 @@ TraceRecorder::ibinary(LOpcode op, bool 
 }
 
 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->ins2i(LIR_eq, ops, (jsword)&js_ObjectOps));
+        guard(true, lir->ins2(LIR_eq, ops, lir->insImmPtr(&js_ObjectOps)));
         return true;
     }
     LIns* n = lir->insLoadi(ops, offsetof(JSObjectOps, newObjectMap));
     if (map->ops->newObjectMap == js_ObjectOps.newObjectMap) {
-        guard(true, lir->ins2i(LIR_eq, n, (jsword)js_ObjectOps.newObjectMap));
+        guard(true, lir->ins2(LIR_eq, n, lir->insImmPtr(&js_ObjectOps.newObjectMap)));
         return true;
     }
     return false;
 }
 
-LIns*
-TraceRecorder::loadObjectClass(LIns* objld)
-{
-    return lir->ins2(LIR_and,
-                     lir->insLoadi(objld,
-                                   offsetof(JSObject, fslots[JSSLOT_CLASS])),
-                     lir->insImmPtr((void *)~3));
-}
-
-bool
-TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns* v_ins)
+void
+TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins)
 {
     if (slot < JS_INITIAL_NSLOTS)
         lir->insStorei(v_ins, 
                 obj_ins, 
                 offsetof(JSObject, fslots) + slot * sizeof(jsval));
-    else
+    else {
+        if (!dslots_ins)
+            dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
         lir->insStorei(v_ins, 
-                lir->insLoadi(obj_ins, offsetof(JSObject, dslots)),
+                dslots_ins,
                 (slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
-    return true;
+    }
 }    
 
-bool
-TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& v_ins)
+void
+TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, LIns*& v_ins)
 {
     if (slot < JS_INITIAL_NSLOTS)
         v_ins = lir->insLoadi(obj_ins, 
                 offsetof(JSObject, fslots) + slot * sizeof(jsval));
-    else
-        v_ins = lir->insLoadi(lir->insLoadi(obj_ins, offsetof(JSObject, dslots)),
+    else {
+        if (!dslots_ins)
+            dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
+        v_ins = lir->insLoadi(
+                dslots_ins,
                 (slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
-    return true;
+    }
 }    
 
 bool
-TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns* v_ins)
+TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns*& dslots_ins, LIns* v_ins)
 {
     // TODO: needs guard?
     if (SPROP_HAS_STUB_SETTER(sprop) && sprop->slot != SPROP_INVALID_SLOT) {
-        return stobj_set_slot(obj_ins, sprop->slot, v_ins);
+        stobj_set_slot(obj_ins, sprop->slot, dslots_ins, v_ins);
+        return true;
     }
     return false;
 }
 
 bool
-TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop, LIns*& v_ins)
+TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop, 
+        LIns*& dslots_ins, LIns*& v_ins)
 {
     // TODO: needs a guard?
     if (SPROP_HAS_STUB_GETTER(sprop)) {
-        if (sprop->slot != SPROP_INVALID_SLOT)
-            return stobj_get_slot(pobj_ins, sprop->slot, v_ins);
+        if (sprop->slot != SPROP_INVALID_SLOT) 
+            stobj_get_slot(pobj_ins, sprop->slot, dslots_ins, v_ins);
         else
             v_ins = lir->insImm(JSVAL_VOID);
         return true;
     }        
     return false;
 }    
 
-bool
-TraceRecorder::box_into_jsval(jsval& v, LIns* cx_ins, LIns* in_ins, LIns*& out_ins)
+LIns*
+TraceRecorder::int_to_jsval(LIns* cx_ins, LIns* i_ins)
+{
+    LIns* args[] = { cx_ins, i_ins };
+    LIns* ret = lir->insCall(F_BoxInt32, args);
+    guard(false, lir->ins_eq0(lir->ins1(LIR_callh, ret)));
+    return ret;
+}
+
+LIns*
+TraceRecorder::jsval_to_int(LIns* v_ins)
+{
+    LIns* ret = lir->insCall(F_UnboxInt32, &v_ins);
+    guard(true, lir->ins_eq0(lir->ins1(LIR_callh, ret)));
+    return ret;
+}    
+
+LIns*
+TraceRecorder::jsval_to_double(LIns* v_ins)
+{
+    guard(true, lir->ins2i(LIR_eq,
+            lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)JSVAL_TAGMASK)),
+            JSVAL_DOUBLE));
+    return lir->insLoadi(lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)~JSVAL_TAGMASK)), 0);
+}    
+
+LIns*
+TraceRecorder::jsval_to_object(LIns* v_ins)
 {
-    if (isInt(v)) {
-        out_ins = lir->ins2i(LIR_or, lir->ins2i(LIR_lsh, in_ins, 1), 1);
-        return true;
-    }
-    return false;
+    LIns* isObj = lir->ins2i(LIR_eq,
+            lir->ins2(LIR_and, v_ins, lir->insImmPtr((void*)JSVAL_TAGMASK)),
+            JSVAL_OBJECT);
+    LIns* isNull = lir->ins_eq0(
+            v_ins);
+    return lir->ins2(LIR_or, isObj, isNull);
+}
+
+
+bool TraceRecorder::guardThatObjectIsDenseArray(JSObject* obj, LIns* obj_ins, LIns*& dslots_ins)
+{
+    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
+        return false;
+    // guard(OBJ_GET_CLASS(obj) == &js_ArrayClass);
+    LIns* class_ins;
+    stobj_get_slot(obj_ins, JSSLOT_CLASS, dslots_ins, class_ins);
+    class_ins = lir->ins2(LIR_and, class_ins, lir->insImmPtr((void*)~3));
+    guard(true, lir->ins2(LIR_eq, class_ins, lir->insImmPtr(&js_ArrayClass)));
+    return true;
+}
+
+bool TraceRecorder::guardDenseArrayIndexWithinBounds(JSObject* obj, jsint idx, 
+        LIns* obj_ins, LIns*& dslots_ins, LIns* idx_ins)
+{
+    jsuint length = ARRAY_DENSE_LENGTH(obj);
+    if (!((jsuint)idx < length && idx < obj->fslots[JSSLOT_ARRAY_LENGTH]))
+        return false;
+    LIns* length_ins;
+    if (!dslots_ins)
+        dslots_ins = lir->insLoadi(obj_ins, offsetof(JSObject, dslots));
+    stobj_get_slot(obj_ins, JSSLOT_ARRAY_LENGTH, dslots_ins, length_ins);
+    // guard(index < length)
+    guard(true, lir->ins2(LIR_lt, idx_ins, length_ins));
+    // guard(index < capacity)
+    guard(false, lir->ins_eq0(dslots_ins));
+    guard(true, lir->ins2(LIR_lt, idx_ins, 
+            lir->insLoadi(dslots_ins, -sizeof(jsval))));
+    return true;
 }
 
 bool TraceRecorder::JSOP_INTERRUPT()
 {
     return false;
 }
 bool TraceRecorder::JSOP_PUSH()
 {
@@ -1118,85 +1177,52 @@ bool TraceRecorder::JSOP_ELEMDEC()
 bool TraceRecorder::JSOP_GETPROP()
 {
     return false;
 }
 bool TraceRecorder::JSOP_SETPROP()
 {
     return false;
 }
-
-bool TraceRecorder::guardAndLoadDenseArray(jsval& aval, jsval& ival,
-                                           LIns*& objld, LIns*& dslotsld)
-{
-    JS_ASSERT(!JSVAL_IS_PRIMITIVE(aval));
-    JSObject *obj = JSVAL_TO_OBJECT(aval);
-    objld = get(&aval);
-    dslotsld = lir->insLoadi(objld, offsetof(JSObject, dslots));
-
-    // guard(OBJ_GET_CLASS(obj) == &js_ArrayClass);
-    guard(true, lir->ins2(LIR_eq,
-                          loadObjectClass(objld),
-                          lir->insImmPtr(&js_ArrayClass)));
-
-
-    jsint i = asInt(ival);
-    jsuint capacity = ARRAY_DENSE_LENGTH(obj);
-    jsint length = obj->fslots[JSSLOT_ARRAY_LENGTH];
-    if ((jsuint)i >= capacity || i >= length)
-        return false;
-
-    // load lengths
-    LIns* lengthld = lir->insLoadi(objld,
-                                   offsetof(JSObject,
-                                            fslots[JSSLOT_ARRAY_LENGTH]));
-
-    // guard(i < length);
-    guard(true, lir->ins2(LIR_lt, get(&ival), lengthld));
-
-    // guard(i < capacity)
-    guard(false, lir->ins_eq0(dslotsld));
-    guard(true, lir->ins2(LIR_lt, get(&ival),
-                          lir->insLoadi(dslotsld, sizeof(jsval) * -1)));
-
-    return true;
-}
 bool TraceRecorder::JSOP_GETELEM()
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
-    if (!isInt(r))
+    /* no guards for type checks, trace specialized this already */
+    if (!isInt(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);
+    if (!guardDenseArrayIndexWithinBounds(obj, idx, obj_ins, dslots_ins, idx_ins))
         return false;
-    jsint i = asInt(r);
-    if (!JSVAL_IS_PRIMITIVE(l)) {
-        JSObject* obj = JSVAL_TO_OBJECT(l);
-        if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
-            LInsp objld, dslotsld;
-            if (!guardAndLoadDenseArray(l, r, objld, dslotsld))
-                return false;
-
-            LIns* slotoff = lir->ins2(LIR_mul, get(&r),
-                                      lir->insImm(sizeof(jsval)));
-
-            // XXX LIR_ld accepts only constant displacements!
-            return false;
-
-            LIns* val = lir->insLoad(LIR_ld, dslotsld, slotoff);
-            if (obj->dslots[i] == JSVAL_HOLE)
-                return false;
-            guard(false, lir->ins2i(LIR_eq, val, JSVAL_HOLE));
-
-            set(&l, val);
-            return true;
-        }
-    }
-    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);
+    if (isInt(v))
+        v_ins = jsval_to_int(v_ins);
+    else if (isDouble(v))
+        v_ins = jsval_to_double(v_ins);
+    else
+        return false; /* we don't know how to convert that type */
+    set(&l, v_ins);
+    return true;
 }
 bool TraceRecorder::JSOP_SETELEM()
 {
+#if 0    
     jsval& v = stackval(-1);
     jsval& r = stackval(-2);
     jsval& l = stackval(-3);
     if (!isInt(r))
         return false;
     jsint i = asInt(r);
     if (!JSVAL_IS_PRIMITIVE(l)) {
         JSObject* obj = JSVAL_TO_OBJECT(l);
@@ -1216,16 +1242,17 @@ bool TraceRecorder::JSOP_SETELEM()
                 return false;
             guard(false, lir->ins2i(LIR_eq, oldval, JSVAL_HOLE));
 
             lir->insStore(get(&v), dslotsld, slotoff);
             set(&l, get(&v));
             return true;
         }
     }
+#endif    
     return false;
 }
 bool TraceRecorder::JSOP_CALLNAME()
 {
     return false;
 }
 bool TraceRecorder::JSOP_CALL()
 {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -129,31 +129,37 @@ class TraceRecorder {
     void var(unsigned n, nanojit::LIns* i);
     nanojit::LIns* stack(int n);
     void stack(int n, nanojit::LIns* i);
     
     bool inc(jsval& v, jsint incr, bool pre);
     bool cmp(nanojit::LOpcode op, bool negate = false);
     bool ibinary(nanojit::LOpcode op, bool ov = false); 
     bool iunary(nanojit::LOpcode op);
-    nanojit::LIns* loadObjectClass(nanojit::LIns* objld);
     
-    // Guard that aval is a dense array, and that ival represents an index
-    // within the length and capacity of the array, and loads obj and
-    // obj->dslots.
-    bool guardAndLoadDenseArray(jsval& aval, jsval& ival,
-                                nanojit::LIns*& objld,
-                                nanojit::LIns*& dslotsld);
+    bool map_is_native(JSObjectMap* map, nanojit::LIns* map_ins);
+    void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, 
+            nanojit::LIns*& dslots_ins, nanojit::LIns* v_ins);
+    void stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot, 
+            nanojit::LIns*& dslots_ins, nanojit::LIns*& v_ins);
+    bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop, 
+            nanojit::LIns*& dslots_ins, nanojit::LIns* v_ins);
+    bool native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins, JSScopeProperty* sprop, 
+            nanojit::LIns*& dslots_ins, nanojit::LIns*& v_ins);
+    bool box_into_jsval(jsval& v, nanojit::LIns* cx_ins, nanojit::LIns* in_ins, 
+            nanojit::LIns*& out_ins);
+    nanojit::LIns* int_to_jsval(nanojit::LIns* cx_ins, nanojit::LIns* i_ins);
+    nanojit::LIns* jsval_to_int(nanojit::LIns* v_ins);
+    nanojit::LIns* jsval_to_double(nanojit::LIns* v_ins);
+    nanojit::LIns* jsval_to_object(nanojit::LIns* v_ins);
 
-    bool map_is_native(JSObjectMap* map, nanojit::LIns* map_ins);
-    bool stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns* v_ins);
-    bool stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& v_ins);
-    bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop, nanojit::LIns* v_ins);
-    bool native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins, JSScopeProperty* sprop, nanojit::LIns*& v_ins);
-    bool box_into_jsval(jsval& v, nanojit::LIns* cx_ins, nanojit::LIns* in_ins, nanojit::LIns*& out_ins);
+    bool guardThatObjectIsDenseArray(JSObject* obj, 
+            nanojit::LIns* obj_ins, nanojit::LIns*& dslots_ins);
+    bool guardDenseArrayIndexWithinBounds(JSObject* obj, jsint idx, 
+            nanojit::LIns* obj_ins, nanojit::LIns*& dslots_ins, nanojit::LIns* idx_ins);
 public:
     TraceRecorder(JSContext* cx, nanojit::Fragmento*, nanojit::Fragment*);
     ~TraceRecorder();
 
     bool loopEdge(JSContext* cx);
     
     bool JSOP_INTERRUPT();
     bool JSOP_PUSH();