Back out bug 604031 for re-landing.
authorDavid Anderson <danderson@mozilla.com>
Tue, 02 Nov 2010 10:56:17 -0700
changeset 57722 c7b08e7b7f949e9d914d7308afd66e50f06ea6e5
parent 57721 d1c6cef6da3a99ea8b80cb447f6bfb83ba7c6022
child 57723 894e42d25be94867a5173e8a10a8c78e644a56b5
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
bugs604031
milestone2.0b8pre
Back out bug 604031 for re-landing.
js/src/jit-test/tests/jaeger/testDenseCallElem.js
js/src/jit-test/tests/jaeger/testPropCallElem.js
js/src/jit-test/tests/jaeger/testPropCallElem2.js
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
deleted file mode 100644
--- a/js/src/jit-test/tests/jaeger/testDenseCallElem.js
+++ /dev/null
@@ -1,89 +0,0 @@
-// vim: set ts=4 sw=4 tw=99 et:
-
-function fillDense(a) {
-}
-
-function testDenseUKeyUArray(a, key) {
-    a.push(function () { return this[3]; });
-    a.push(function () { return this[4]; });
-    a.push(function() { return this[5]; });
-    a.push(20);
-    a.push("hi");
-    a.push(500);
-    assertEq(a[key](), 20);
-    assertEq(a[key + 1](), "hi");
-    assertEq(a[key + 2](), 500);
-}
-
-function testDenseVKeyUArray(a) {
-    a.push(function () { return this[3]; });
-    a.push(function () { return this[4]; });
-    a.push(function() { return this[5]; });
-    a.push(20);
-    a.push("hi");
-    a.push(500);
-    var key = a.length & 1;
-    assertEq(a[key](), 20);
-    assertEq(a[(key + 1) & 3](), "hi");
-    assertEq(a[(key + 2) & 3](), 500);
-}
-
-function testDenseKKeyUArray(a, key) {
-    a.push(function () { return this[3]; });
-    a.push(function () { return this[4]; });
-    a.push(function() { return this[5]; });
-    a.push(20);
-    a.push("hi");
-    a.push(500);
-    assertEq(a[0](), 20);
-    assertEq(a[1](), "hi");
-    assertEq(a[2](), 500);
-}
-
-function testDenseUKeyVArray(key) {
-    var a = [function () { return this[3]; },
-             function () { return this[4]; },
-             function() { return this[5]; },
-             20,
-             "hi",
-             500];
-    assertEq(a[key](), 20);
-    assertEq(a[key + 1](), "hi");
-    assertEq(a[key + 2](), 500);
-}
-
-function testDenseVKeyVArray() {
-    var a = [function () { return this[3]; },
-             function () { return this[4]; },
-             function() { return this[5]; },
-             20,
-             "hi",
-             500];
-    var key = a.length & 1;
-    assertEq(a[key](), 20);
-    assertEq(a[(key + 1) & 3](), "hi");
-    assertEq(a[(key + 2) & 3](), 500);
-}
-
-function testDenseKKeyVArray() {
-    var a = [function () { return this[3]; },
-             function () { return this[4]; },
-             function() { return this[5]; },
-             20,
-             "hi",
-             500];
-    assertEq(a[0](), 20);
-    assertEq(a[1](), "hi");
-    assertEq(a[2](), 500);
-}
-
-for (var i = 0; i < 5; i++) {
-    testDenseUKeyUArray([], 0);
-    testDenseVKeyUArray([]);
-    testDenseKKeyUArray([]);
-    testDenseUKeyVArray(0);
-    testDenseVKeyVArray();
-    testDenseKKeyVArray();
-}
-
-
deleted file mode 100644
--- a/js/src/jit-test/tests/jaeger/testPropCallElem.js
+++ /dev/null
@@ -1,93 +0,0 @@
-// vim: set ts=4 sw=4 tw=99 et:
-
-function testUKeyUObject(a, key1, key2, key3) {
-    a.a = function () { return this.d; }
-    a.b = function () { return this.e; }
-    a.c = function() { return this.f; }
-    a.d = 20;
-    a.e = "hi";
-    a.f = 500;
-    assertEq(a[key1](), 20);
-    assertEq(a[key2](), "hi");
-    assertEq(a[key3](), 500);
-}
-
-function testVKeyUObject(a, key1, key2, key3) {
-    a.a = function () { return this.d; }
-    a.b = function () { return this.e; }
-    a.c = function() { return this.f; }
-    a.d = 20;
-    a.e = "hi";
-    a.f = 500;
-    assertEq(a["" + key1](), 20);
-    assertEq(a["" + key2](), "hi");
-    assertEq(a["" + key3](), 500);
-}
-
-function testKKeyUObject(a) {
-    a.a = function () { return this.d; }
-    a.b = function () { return this.e; }
-    a.c = function() { return this.f; }
-    a.d = 20;
-    a.e = "hi";
-    a.f = 500;
-    var key1 = "a";
-    var key2 = "b";
-    var key3 = "c";
-    assertEq(a[key1](), 20);
-    assertEq(a[key2](), "hi");
-    assertEq(a[key3](), 500);
-}
-
-function testUKeyVObject(key1, key2, key3) {
-    a = { a: function () { return this.d; },
-          b: function () { return this.e; },
-          c: function () { return this.f; },
-          d: 20,
-          e: "hi",
-          f: 500
-    };
-    assertEq(a[key1](), 20);
-    assertEq(a[key2](), "hi");
-    assertEq(a[key3](), 500);
-}
-
-function testVKeyVObject(key1, key2, key3) {
-    a = { a: function () { return this.d; },
-          b: function () { return this.e; },
-          c: function () { return this.f; },
-          d: 20,
-          e: "hi",
-          f: 500
-    };
-    assertEq(a["" + key1](), 20);
-    assertEq(a["" + key2](), "hi");
-    assertEq(a["" + key3](), 500);
-}
-
-function testKKeyVObject(a) {
-    a = { a: function () { return this.d; },
-          b: function () { return this.e; },
-          c: function () { return this.f; },
-          d: 20,
-          e: "hi",
-          f: 500
-    };
-    var key1 = "a";
-    var key2 = "b";
-    var key3 = "c";
-    assertEq(a[key1](), 20);
-    assertEq(a[key2](), "hi");
-    assertEq(a[key3](), 500);
-}
-
-for (var i = 0; i < 5; i++) {
-    testUKeyUObject({}, "a", "b", "c");
-    testVKeyUObject({}, "a", "b", "c");
-    testKKeyUObject({});
-    testUKeyVObject("a", "b", "c");
-    testVKeyVObject("a", "b", "c");
-    testKKeyVObject();
-}
-
-
deleted file mode 100644
--- a/js/src/jit-test/tests/jaeger/testPropCallElem2.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// vim: set ts=4 sw=4 tw=99 et:
-
-function testUKeyUObject(a, key1, key2, key3) {
-    a.a = function () { return this.d; }
-    a.b = function () { return this.e; }
-    a.c = function() { return this.f; }
-    a.d = 20;
-    a.e = "hi";
-    a.f = 500;
-    delete a["b"];
-    Object.defineProperty(a, "b", { get: function () { return function () { return this.e; } } });
-    assertEq(a[key1](), 20);
-    assertEq(a[key2](), "hi");
-    assertEq(a[key3](), 500);
-}
-
-for (var i = 0; i < 5; i++)
-    testUKeyUObject({}, "a", "b", "c");
-
-
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1265,17 +1265,17 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_GETPROP)
 
           BEGIN_CASE(JSOP_LENGTH)
             if (!jsop_length())
                 return Compile_Error;
           END_CASE(JSOP_LENGTH)
 
           BEGIN_CASE(JSOP_GETELEM)
-            if (!jsop_getelem(false))
+            if (!jsop_getelem())
                 return Compile_Error;
           END_CASE(JSOP_GETELEM)
 
           BEGIN_CASE(JSOP_SETELEM)
             if (!jsop_setelem())
                 return Compile_Error;
           END_CASE(JSOP_SETELEM);
 
@@ -1850,17 +1850,21 @@ mjit::Compiler::generateMethod()
           }
           END_CASE(JSOP_CALLUPVAR)
 
           BEGIN_CASE(JSOP_UINT24)
             frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
           END_CASE(JSOP_UINT24)
 
           BEGIN_CASE(JSOP_CALLELEM)
-            jsop_getelem(true);
+            prepareStubCall(Uses(2));
+            stubCall(stubs::CallElem);
+            frame.popn(2);
+            frame.pushSynced();
+            frame.pushSynced();
           END_CASE(JSOP_CALLELEM)
 
           BEGIN_CASE(JSOP_STOP)
             /* Safe point! */
             emitReturn(NULL);
             goto done;
           END_CASE(JSOP_STOP)
 
@@ -4814,18 +4818,8 @@ mjit::Compiler::constructThis()
     prepareStubCall(Uses(0));
     if (protoReg != Registers::ArgReg1)
         masm.move(protoReg, Registers::ArgReg1);
     stubCall(stubs::CreateThis);
     frame.freeReg(protoReg);
     return true;
 }
 
-void
-mjit::Compiler::jsop_callelem_slow()
-{
-    prepareStubCall(Uses(2));
-    stubCall(stubs::CallElem);
-    frame.popn(2);
-    frame.pushSynced();
-    frame.pushSynced();
-}
-
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -368,17 +368,16 @@ class Compiler : public BaseCompiler
     void jsop_eleminc(JSOp op, VoidStub);
     void jsop_getgname(uint32 index);
     void jsop_getgname_slow(uint32 index);
     void jsop_setgname(uint32 index);
     void jsop_setgname_slow(uint32 index);
     void jsop_bindgname();
     void jsop_setelem_slow();
     void jsop_getelem_slow();
-    void jsop_callelem_slow();
     void jsop_unbrand();
     bool jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true);
     bool jsop_length();
     bool jsop_setprop(JSAtom *atom, bool usePropCache = true);
     void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);
     bool jsop_callprop_slow(JSAtom *atom);
     bool jsop_callprop(JSAtom *atom);
     bool jsop_callprop_obj(JSAtom *atom);
@@ -435,17 +434,17 @@ class Compiler : public BaseCompiler
     void jsop_not();
     void jsop_typeof();
     bool booleanJumpScript(JSOp op, jsbytecode *target);
     bool jsop_ifneq(JSOp op, jsbytecode *target);
     bool jsop_andor(JSOp op, jsbytecode *target);
     void jsop_arginc(JSOp op, uint32 slot, bool popped);
     void jsop_localinc(JSOp op, uint32 slot, bool popped);
     bool jsop_setelem();
-    bool jsop_getelem(bool isCall);
+    bool jsop_getelem();
     bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
     void jsop_stricteq(JSOp op);
     bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     void jsop_pos();
 
 #define STUB_CALL_TYPE(type)                                            \
     Call stubCall(type stub) {                                          \
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1370,26 +1370,23 @@ IsCacheableGetElem(FrameEntry *obj, Fram
     // obj[obj] is not allowed, since it will never optimize.
     if (obj->hasSameBacking(id))
         return false;
 
     return true;
 }
 
 bool
-mjit::Compiler::jsop_getelem(bool isCall)
+mjit::Compiler::jsop_getelem()
 {
     FrameEntry *obj = frame.peek(-2);
     FrameEntry *id = frame.peek(-1);
 
     if (!IsCacheableGetElem(obj, id)) {
-        if (isCall)
-            jsop_callelem_slow();
-        else
-            jsop_getelem_slow();
+        jsop_getelem_slow();
         return true;
     }
 
     GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
 
     // Pin the top of the stack to avoid spills, before allocating registers.
     MaybeRegisterID pinnedIdData = frame.maybePinData(id);
     MaybeRegisterID pinnedIdType = frame.maybePinType(id);
@@ -1407,24 +1404,16 @@ mjit::Compiler::jsop_getelem(bool isCall
         // that needs to change is a little code movement.
         stubcc.linkExit(guard, Uses(2));
         objTypeGuard = stubcc.masm.jump();
     }
 
     // Get a mutable register for the object. This will be the data reg.
     ic.objReg = frame.copyDataIntoReg(obj);
 
-    // For potential dense array calls, grab an extra reg to save the
-    // outgoing object.
-    MaybeRegisterID thisReg;
-    if (isCall && id->mightBeType(JSVAL_TYPE_INT32)) {
-        thisReg = frame.allocReg();
-        masm.move(ic.objReg, thisReg.reg());
-    }
-
     // Get a mutable register for pushing the result type. We kill two birds
     // with one stone by making sure, if the key type is not known, to be loaded
     // into this register. In this case it is both an input and an output.
     frame.maybeUnpinReg(pinnedIdType);
     if (id->isConstant() || id->isTypeKnown())
         ic.typeReg = frame.allocReg();
     else
         ic.typeReg = frame.copyTypeIntoReg(id);
@@ -1460,54 +1449,39 @@ mjit::Compiler::jsop_getelem(bool isCall
 
         Int32Key key = id->isConstant()
                        ? Int32Key::FromConstant(id->getValue().toInt32())
                        : Int32Key::FromRegister(ic.id.dataReg());
 
         Assembler::FastArrayLoadFails fails =
             masm.fastArrayLoad(ic.objReg, key, ic.typeReg, ic.objReg);
 
-        // Store the object back to sp[-1] for calls. This must occur after
-        // all guards because otherwise sp[-1] will be clobbered.
-        if (isCall) {
-            Address thisSlot = frame.addressOf(id);
-            masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), thisReg.reg(), thisSlot);
-            frame.freeReg(thisReg.reg());
-        }
-
         stubcc.linkExitDirect(fails.rangeCheck, ic.slowPathStart);
         stubcc.linkExitDirect(fails.holeCheck, ic.slowPathStart);
     } else {
         // The type is known to not be dense-friendly ahead of time, so always
         // fall back to a slow path.
         ic.claspGuard = masm.jump();
         stubcc.linkExitDirect(ic.claspGuard, ic.slowPathStart);
     }
 
     stubcc.leave();
     if (objTypeGuard.isSet())
         objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
 #ifdef JS_POLYIC
     passICAddress(&ic);
-    ic.slowPathCall = isCall
-                      ? stubcc.call(ic::CallElement)
-                      : stubcc.call(ic::GetElement);
+    ic.slowPathCall = stubcc.call(ic::GetElement);
 #else
-    if (isCall)
-        stubcc.call(stubs::CallElem);
-    else
-        stubcc.call(stubs::GetElem);
+    stubcc.call(stubs::GetElem);
 #endif
 
     ic.fastPathRejoin = masm.label();
 
     frame.popn(2);
     frame.pushRegs(ic.typeReg, ic.objReg);
-    if (isCall)
-        frame.pushSynced();
 
     stubcc.rejoin(Changes(2));
 
 #ifdef JS_POLYIC
     if (!getElemICs.append(ic))
         return false;
 #endif
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -45,17 +45,16 @@
 #include "assembler/assembler/RepatchBuffer.h"
 #include "jsscope.h"
 #include "jsnum.h"
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jspropertycache.h"
 #include "jspropertycacheinlines.h"
-#include "jsinterpinlines.h"
 #include "jsautooplen.h"
 
 #if defined JS_POLYIC
 
 using namespace js;
 using namespace js::mjit;
 using namespace js::mjit::ic;
 
@@ -1940,42 +1939,33 @@ BaseIC::shouldUpdate(JSContext *cx)
 }
 
 static void JS_FASTCALL
 DisabledGetElem(VMFrame &f, ic::GetElementIC *ic)
 {
     stubs::GetElem(f);
 }
 
-static void JS_FASTCALL
-DisabledCallElem(VMFrame &f, ic::GetElementIC *ic)
-{
-    stubs::CallElem(f);
-}
-
 bool
 GetElementIC::shouldUpdate(JSContext *cx)
 {
     if (!hit) {
         hit = true;
         spew(cx, "ignored", "first hit");
         return false;
     }
     JS_ASSERT(stubsGenerated < MAX_GETELEM_IC_STUBS);
     return true;
 }
 
 LookupStatus
 GetElementIC::disable(JSContext *cx, const char *reason)
 {
     slowCallPatched = true;
-    void *stub = (op == JSOP_GETELEM)
-                 ? JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem)
-                 : JS_FUNC_TO_DATA_PTR(void *, DisabledCallElem);
-    BaseIC::disable(cx, reason, stub);
+    BaseIC::disable(cx, reason, JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem));
     return Lookup_Uncacheable;
 }
 
 LookupStatus
 GetElementIC::error(JSContext *cx)
 {
     disable(cx, "error");
     return Lookup_Error;
@@ -2058,23 +2048,16 @@ GetElementIC::attachGetProp(JSContext *c
         holderReg = typeReg;
         masm.move(ImmPtr(holder), holderReg);
         typeRegHasBaseShape = false;
 
         // Guard on the holder's shape.
         protoGuard = masm.guardShape(holderReg, holder);
     }
 
-    if (op == JSOP_CALLELEM) {
-        // Emit a write of |obj| to the top of the stack, before we lose it.
-        Value *thisVp = &cx->regs->sp[-1];
-        Address thisSlot(JSFrameReg, JSStackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
-        masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), objReg, thisSlot);
-    }
-
     // Load the value.
     const Shape *shape = getprop.shape;
     masm.loadObjProp(holder, holderReg, shape, typeReg, objReg);
 
     Jump done = masm.jump();
 
     PICLinker buffer(cx, *this);
     if (!buffer.init(masm))
@@ -2085,19 +2068,19 @@ GetElementIC::attachGetProp(JSContext *c
     buffer.maybeLink(atomTypeGuard, slowPathStart);
     buffer.link(shapeGuard, slowPathStart);
     buffer.maybeLink(protoGuard, slowPathStart);
     buffer.link(done, fastPathRejoin);
 
     CodeLocationLabel cs = buffer.finalizeCodeAddendum();
 #if DEBUG
     char *chars = js_DeflateString(cx, v.toString()->chars(), v.toString()->length());
-    JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
-               js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(),
-               cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp()));
+    JaegerSpew(JSpew_PICs, "generated getelem stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
+               cs.executableAddress(), id, chars, holder->shape(), cx->fp()->script()->filename,
+               js_FramePCToLineNumber(cx, cx->fp()));
     cx->free(chars);
 #endif
 
     // Update the inline guards, if needed.
     if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalClaspGuard()) {
         PICRepatchBuffer repatcher(*this, fastPathStart);
 
         if (shouldPatchInlineTypeGuard()) {
@@ -2171,73 +2154,16 @@ LookupStatus
 GetElementIC::update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp)
 {
     if (v.isString())
         return attachGetProp(cx, obj, v, id, vp);
     return disable(cx, "unhandled object and key type");
 }
 
 void JS_FASTCALL
-ic::CallElement(VMFrame &f, ic::GetElementIC *ic)
-{
-    JSContext *cx = f.cx;
-
-    // Right now, we don't optimize for strings.
-    if (!f.regs.sp[-2].isObject()) {
-        ic->disable(cx, "non-object");
-        stubs::CallElem(f);
-        return;
-    }
-
-    Value thisv = f.regs.sp[-2];
-    JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
-    if (!thisObj)
-        THROW();
-
-    jsid id;
-    Value idval = f.regs.sp[-1];
-    if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32()))
-        id = INT_TO_JSID(idval.toInt32());
-    else if (!js_InternNonIntElementId(cx, thisObj, idval, &id))
-        THROW();
-
-    if (ic->shouldUpdate(cx)) {
-#ifdef DEBUG
-        f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC);
-#endif
-        LookupStatus status = ic->update(cx, thisObj, idval, id, &f.regs.sp[-2]);
-        if (status != Lookup_Uncacheable) {
-            if (status == Lookup_Error)
-                THROW();
-
-            // If the result can be cached, the value was already retrieved.
-            JS_ASSERT(!f.regs.sp[-2].isMagic());
-            f.regs.sp[-1].setObject(*thisObj);
-            return;
-        }
-    }
-
-    /* Get or set the element. */
-    if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &f.regs.sp[-2]))
-        THROW();
-
-#if JS_HAS_NO_SUCH_METHOD
-    if (JS_UNLIKELY(f.regs.sp[-2].isUndefined()) && thisv.isObject()) {
-        f.regs.sp[-2] = f.regs.sp[-1];
-        f.regs.sp[-1].setObject(*thisObj);
-        if (!js_OnUnknownMethod(cx, f.regs.sp - 2))
-            THROW();
-    } else
-#endif
-    {
-        f.regs.sp[-1] = thisv;
-    }
-}
-
-void JS_FASTCALL
 ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
 {
     JSContext *cx = f.cx;
 
     // Right now, we don't optimize for strings.
     if (!f.regs.sp[-2].isObject()) {
         ic->disable(cx, "non-object");
         stubs::GetElem(f);
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -496,17 +496,16 @@ struct PICInfo : public BasePolyIC {
 void PurgePICs(JSContext *cx, JSScript *script);
 void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
 void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
-void JS_FASTCALL CallElement(VMFrame &f, ic::GetElementIC *);
 template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
 #endif
 
 } /* namespace ic */
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* jsjaeger_poly_ic_h__ */