Trace getting String.length (484332, r=brendan).
authorAndreas Gal <gal@mozilla.com>
Tue, 21 Apr 2009 18:56:24 -0700
changeset 27637 a0127eb0db9da169b4c6606a98d65bfdd402c046
parent 27636 ab3af3308c0e7c6fa3e05b65aee4e25a2fbd0f8e
child 27638 ca40d9bb095441ae82a8905cf9738b0a52fdb7c0
push id6664
push userrsayre@mozilla.com
push dateWed, 22 Apr 2009 17:56:08 +0000
treeherdermozilla-central@ddd5fcb96d72 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs484332
milestone1.9.2a1pre
Trace getting String.length (484332, r=brendan).
js/src/jstracer.cpp
js/src/jstracer.h
js/src/trace-test.js
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6392,16 +6392,40 @@ TraceRecorder::getThis(LIns*& this_ins)
         LIns* map_ins = lir->insLoad(LIR_ldp, this_ins, (int)offsetof(JSObject, map));
         LIns* ops_ins = lir->insLoad(LIR_ldp, map_ins, (int)offsetof(JSObjectMap, ops));
         LIns* op_ins = lir->insLoad(LIR_ldp, ops_ins, (int)offsetof(JSObjectOps, thisObject));
         guard(true, lir->ins_eq0(op_ins), MISMATCH_EXIT);
     }
     return true;
 }
 
+
+LIns*
+TraceRecorder::getStringLength(LIns* str_ins)
+{
+    LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, length));
+
+    LIns* masked_len_ins = lir->ins2(LIR_piand,
+                                     len_ins,
+                                     INS_CONSTPTR((void *)JSSTRING_LENGTH_MASK));
+
+    return
+        lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
+                                               len_ins,
+                                               INS_CONSTPTR((void*)JSSTRFLAG_DEPENDENT))),
+                        masked_len_ins,
+                        lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
+                                                               len_ins,
+                                                               INS_CONSTPTR((void*)JSSTRFLAG_PREFIX))),
+                                        lir->ins2(LIR_piand,
+                                                  len_ins,
+                                                  INS_CONSTPTR((void*)JSSTRDEP_LENGTH_MASK)),
+                                        masked_len_ins));
+}
+
 JS_REQUIRES_STACK bool
 TraceRecorder::guardClass(JSObject* obj, LIns* obj_ins, JSClass* clasp, VMSideExit* exit)
 {
     bool cond = STOBJ_GET_CLASS(obj) == clasp;
 
     LIns* class_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, classword));
     class_ins = lir->ins2(LIR_piand, class_ins, lir->insImm(~JSSLOT_CLASS_MASK_BITS));
 
@@ -8370,29 +8394,39 @@ TraceRecorder::prop(JSObject* obj, LIns*
         if (setflags && (sprop->attrs & JSPROP_READONLY))
             ABORT_TRACE("writing to a readonly property");
         if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
             // FIXME 450335: generalize this away from regexp built-in getters.
             if (setflags == 0 &&
                 sprop->getter == js_RegExpClass.getProperty &&
                 sprop->shortid < 0) {
                 if (sprop->shortid == REGEXP_LAST_INDEX)
-                    ABORT_TRACE("can't trace regexp.lastIndex yet");
+                    ABORT_TRACE("can't trace RegExp.lastIndex yet");
                 LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
                 v_ins = lir->insCall(&js_CallGetter_ci, args);
                 guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
                 /*
                  * BIG FAT WARNING: This snapshot cannot be a BRANCH_EXIT, since
                  * the value to the top of the stack is not the value we unbox.
                  */
                 unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
                             v_ins,
                             snapshot(MISMATCH_EXIT));
                 return true;
             }
+            if (setflags == 0 &&
+                sprop->getter == js_StringClass.getProperty &&
+                sprop->id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
+                if (!guardClass(obj, obj_ins, &js_StringClass, snapshot(MISMATCH_EXIT)))
+                    ABORT_TRACE("can't trace String.length on non-String objects");
+                LIns* str_ins = stobj_get_fslot(obj_ins, JSSLOT_PRIVATE);
+                str_ins = lir->ins2(LIR_piand, str_ins, INS_CONSTPTR((void*)(~JSVAL_TAGMASK)));
+                v_ins = lir->ins1(LIR_i2f, getStringLength(str_ins));
+                return true;
+            }
             ABORT_TRACE("non-stub getter");
         }
         if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
             ABORT_TRACE("no valid slot");
         slot = sprop->slot;
     } else {
         if (!PCVAL_IS_SLOT(pcval))
             ABORT_TRACE("PCE is not a slot");
@@ -10029,37 +10063,17 @@ TraceRecorder::record_JSOP_INT32()
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_LENGTH()
 {
     jsval& l = stackval(-1);
     if (JSVAL_IS_PRIMITIVE(l)) {
         if (!JSVAL_IS_STRING(l))
             ABORT_TRACE("non-string primitive JSOP_LENGTH unsupported");
-        LIns* str_ins = get(&l);
-        LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, length));
-
-        LIns* masked_len_ins = lir->ins2(LIR_piand,
-                                         len_ins,
-                                         INS_CONSTPTR(reinterpret_cast<void *>(JSSTRING_LENGTH_MASK)));
-
-        LIns* choose_len_ins =
-            lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
-                                                   len_ins,
-                                                   INS_CONSTPTR(reinterpret_cast<void *>(JSSTRFLAG_DEPENDENT)))),
-                            masked_len_ins,
-                            lir->ins_choose(lir->ins_eq0(lir->ins2(LIR_piand,
-                                                                   len_ins,
-                                                                   INS_CONSTPTR(reinterpret_cast<void *>(JSSTRFLAG_PREFIX)))),
-                                            lir->ins2(LIR_piand,
-                                                      len_ins,
-                                                      INS_CONSTPTR(reinterpret_cast<void *>(JSSTRDEP_LENGTH_MASK))),
-                                            masked_len_ins));
-
-        set(&l, lir->ins1(LIR_i2f, choose_len_ins));
+        set(&l, lir->ins1(LIR_i2f, getStringLength(get(&l))));
         return true;
     }
 
     JSObject* obj = JSVAL_TO_OBJECT(l);
     LIns* obj_ins = get(&l);
     LIns* v_ins;
     if (OBJ_IS_ARRAY(cx, obj)) {
         if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -521,16 +521,18 @@ class TraceRecorder : public avmplus::GC
                                    nanojit::LIns*& dslots_ins);
     nanojit::LIns* stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot,
                                   nanojit::LIns*& dslots_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);
 
+    nanojit::LIns* getStringLength(nanojit::LIns* str_ins);
+
     JS_REQUIRES_STACK bool name(jsval*& vp);
     JS_REQUIRES_STACK bool prop(JSObject* obj, nanojit::LIns* obj_ins, uint32& slot,
                                 nanojit::LIns*& v_ins);
     JS_REQUIRES_STACK bool elem(jsval& oval, jsval& idx, jsval*& vp, nanojit::LIns*& v_ins,
                                 nanojit::LIns*& addr_ins);
     JS_REQUIRES_STACK bool getProp(JSObject* obj, nanojit::LIns* obj_ins);
     JS_REQUIRES_STACK bool getProp(jsval& v);
     JS_REQUIRES_STACK bool getThis(nanojit::LIns*& this_ins);
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -5027,16 +5027,24 @@ function testFunctionIdentityChange()
 testFunctionIdentityChange.expected = true;
 testFunctionIdentityChange.jitstats = {
   recorderStarted: 2,
   traceCompleted: 2,
   sideExitIntoInterpreter: 3
 };
 test(testFunctionIdentityChange);
 
+function testStringObjectLength() {
+    var x = new String("foo"), y = 0;
+    for (var i = 0; i < 10; ++i)
+        y = x.length;
+    return y;
+}
+testStringObjectLength.expected = 3;
+test(testStringObjectLength);
 
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *