Bug 557353 - JM: PIC for object-wrapped string length. r=dvander
authorTom Schuster <evilpies@gmail.com>
Thu, 30 Dec 2010 13:25:03 -0600
changeset 59970 feb28ec64b743cbdbfb3d3283a19e072f258a926
parent 59969 c5d43dfafcbc17707bed4c1849a68a72e2676e8b
child 59971 b139e168fbf00b363a15edca33ac10b935289176
push id17820
push usercleary@mozilla.com
push dateTue, 04 Jan 2011 21:40:57 +0000
treeherdermozilla-central@969691cfe40e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs557353
milestone2.0b9pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 557353 - JM: PIC for object-wrapped string length. r=dvander
js/src/jit-test/tests/pic/length_string_object.js
js/src/jsobj.h
js/src/methodjit/PolyIC.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/pic/length_string_object.js
@@ -0,0 +1,36 @@
+//length, string, object
+
+var expected = "3,6,4,3,6,4,3,6,4,3,6,4,";
+var actual = '';
+
+function f() {
+    var ss = [new String("abc"), new String("foobar"), new String("quux")];
+
+    for (var i = 0; i < 12; ++i) {
+        actual += ss[i%3].length + ',';
+    }
+}
+
+f();
+
+assertEq(actual, expected);
+
+
+function g(s) {
+    return new String(s).length;
+}
+
+assertEq(g("x"), 1); // Warm-up
+assertEq(g("x"), 1); // Create IC
+assertEq(g("x"), 1); // Test IC
+
+function h(s) {
+    var x = new String(s);
+    for (var i = 0; i < 100; i++)
+        x[i] = i;
+    return x.length;
+}
+
+assertEq(h("x"), 1);
+assertEq(h("x"), 1);
+assertEq(h("x"), 1);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -295,16 +295,17 @@ struct JSObject : js::gc::Cell {
     /*
      * TraceRecorder must be a friend because it generates code that
      * manipulates JSObjects, which requires peeking under any encapsulation.
      * ValidateWriter must be a friend because it works in tandem with
      * TraceRecorder.
      */
     friend class js::TraceRecorder;
     friend class nanojit::ValidateWriter;
+    friend class GetPropCompiler;
 
     /*
      * Private pointer to the last added property and methods to manipulate the
      * list it links among properties in this scope. The {remove,insert} pair
      * for DictionaryProperties assert that the scope is in dictionary mode and
      * any reachable properties are flagged as dictionary properties.
      *
      * NB: these private methods do *not* update this scope's shape to track
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -821,17 +821,18 @@ class GetPropCompiler : public PICStubCo
         Assembler masm;
 
         Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
 
         masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
         masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
                     pic.objReg);
         masm.move(pic.objReg, pic.shapeReg);
-        Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, Imm32(1));
+        Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
+                                            Imm32(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
         masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg);
         
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
@@ -889,16 +890,51 @@ class GetPropCompiler : public PICStubCo
 
         patchPreviousToHere(start);
 
         disable("array length done");
 
         return Lookup_Cacheable;
     }
 
+    LookupStatus generateStringObjLengthStub()
+    {
+        Assembler masm;
+
+        Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
+        masm.loadPayload(Address(pic.objReg, JSObject::getFixedSlotOffset(
+                         JSObject::JSSLOT_PRIMITIVE_THIS)), pic.objReg);
+        masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
+        masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
+        masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
+        Jump done = masm.jump();
+
+        PICLinker buffer(masm, pic);
+        if (!buffer.init(cx))
+            return error();
+
+        if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
+            !buffer.verifyRange(f.jit())) {
+            return disable("code memory is out of range");
+        }
+
+        buffer.link(notStringObj, pic.slowPathStart);
+        buffer.link(done, pic.fastPathRejoin);
+
+        CodeLocationLabel start = buffer.finalize();
+        JaegerSpew(JSpew_PICs, "generate string object length stub at %p\n",
+                   start.executableAddress());
+
+        patchPreviousToHere(start);
+
+        disable("string object length done");
+
+        return Lookup_Cacheable;
+    }
+
     LookupStatus generateStringCallStub()
     {
         JS_ASSERT(pic.hasTypeCheck());
         JS_ASSERT(pic.kind == ic::PICInfo::CALL);
 
         if (!f.fp()->script()->compileAndGo)
             return disable("String.prototype without compile-and-go");
 
@@ -1657,28 +1693,35 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
             LookupStatus status = cc.generateStringLengthStub();
             if (status == Lookup_Error)
                 THROW();
             JSString *str = f.regs.sp[-1].toString();
             f.regs.sp[-1].setInt32(str->length());
             return;
         } else if (!f.regs.sp[-1].isPrimitive()) {
             JSObject *obj = &f.regs.sp[-1].toObject();
-            if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) {
+            if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden()) ||
+                obj->isString()) {
                 GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
                 if (obj->isArray()) {
                     LookupStatus status = cc.generateArrayLengthStub();
                     if (status == Lookup_Error)
                         THROW();
                     f.regs.sp[-1].setNumber(obj->getArrayLength());
                 } else if (obj->isArguments()) {
                     LookupStatus status = cc.generateArgsLengthStub();
                     if (status == Lookup_Error)
                         THROW();
                     f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength()));
+                } else if (obj->isString()) {
+                    LookupStatus status = cc.generateStringObjLengthStub();
+                    if (status == Lookup_Error)
+                        THROW();
+                    JSString *str = obj->getPrimitiveThis().toString();
+                    f.regs.sp[-1].setInt32(str->length());
                 }
                 return;
             }
         }
         atom = f.cx->runtime->atomState.lengthAtom;
     }
 
     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);