Bug 616744 - Add GetElement PIC for arguments objects r=dvander
authorTom Schuster <evilpies@gmail.com>
Wed, 27 Jul 2011 15:39:55 +0200
changeset 73422 da50621162f38bf00c3c0815e8e8346b12bf8908
parent 73421 6b7ac58e6e962e658305db2bdcbaad3b35d3fb46
child 73423 32c49de2fe07316a6a6e60da2ff9c26cddf3da87
child 73437 169712c5c68b0d32744e101c0fca0776e8650c4f
push id20861
push usereakhgari@mozilla.com
push dateWed, 27 Jul 2011 18:02:44 +0000
treeherdermozilla-central@32c49de2fe07 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs616744
milestone8.0a1
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 616744 - Add GetElement PIC for arguments objects r=dvander
js/src/jit-test/tests/pic/arguments.js
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
js/src/vm/ArgumentsObject.h
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/pic/arguments.js
@@ -0,0 +1,23 @@
+function f() {
+    var args = arguments, r;
+
+    for (var i = 0; i < args.length; i++)
+        r = args[i];
+
+    return r;
+}
+
+assertEq(f.apply(null, [1, 2, 3, 4, 5, 6]), 6)
+assertEq(f.apply(null, [1, 2, 3, 4, 5]), 5)
+assertEq(f.apply(null, [1, 2, 3, 4]), 4)
+
+function g(arg) {
+    var r;
+    for (var i = 0; i < arg.length; i++)
+        r = arg[i];
+    return r;
+}
+
+assertEq(g((function () arguments).call(null, 1, 2, 3)), 3);
+assertEq(g(new Float32Array(3)), 0.0);
+assertEq(g([1, 2, 3, 4]), 4);
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -2292,16 +2292,164 @@ GetElementIC::attachGetProp(JSContext *c
     if (shape->isMethod())
         *vp = ObjectValue(shape->methodObject());
     else
         *vp = holder->getSlot(shape->slot);
 
     return Lookup_Cacheable;
 }
 
+LookupStatus
+GetElementIC::attachArguments(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp)
+{
+    if (!v.isInt32())
+        return disable(cx, "arguments object with non-integer key");
+
+    if (op == JSOP_CALLELEM)
+        return disable(cx, "arguments object with call");
+
+    JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32);
+
+    Assembler masm;
+
+    Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass());
+
+    masm.move(objReg, typeReg);
+    masm.load32(Address(objReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), 
+                objReg);
+    Jump overridden = masm.branchTest32(Assembler::NonZero, objReg,
+                                        Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
+    masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), objReg);
+
+    Jump outOfBounds;
+    if (idRemat.isConstant()) {
+        outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32()));
+    } else {
+        outOfBounds = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg());
+    }
+
+    masm.loadPayload(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg);
+    if (idRemat.isConstant()) {
+        Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value));
+        masm.loadTypeTag(slot, objReg);
+    } else {
+        BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE, 
+                       offsetof(ArgumentsData, slots));
+        masm.loadTypeTag(slot, objReg);
+    }    
+    Jump holeCheck = masm.branchPtr(Assembler::Equal, objReg, ImmType(JSVAL_TYPE_MAGIC));
+
+    Address privateData(typeReg, offsetof(JSObject, privateData));
+    Jump liveArguments = masm.branchPtr(Assembler::NotEqual, privateData, ImmPtr(0));
+   
+    masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg);
+
+    if (idRemat.isConstant()) {
+        Address slot(objReg, offsetof(ArgumentsData, slots) + v.toInt32() * sizeof(Value));
+        masm.loadValueAsComponents(slot, typeReg, objReg);           
+    } else {
+        BaseIndex slot(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE, 
+                       offsetof(ArgumentsData, slots));
+        masm.loadValueAsComponents(slot, typeReg, objReg);
+    }
+
+    Jump done = masm.jump();
+
+    liveArguments.linkTo(masm.label(), &masm);
+
+    masm.loadPtr(privateData, typeReg);
+
+    Address fun(typeReg, StackFrame::offsetOfExec());
+    masm.loadPtr(fun, objReg);
+
+    Address nargs(objReg, offsetof(JSFunction, nargs));
+    masm.load16(nargs, objReg);
+
+    Jump notFormalArg;
+    if (idRemat.isConstant())
+        notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, Imm32(v.toInt32()));
+    else
+        notFormalArg = masm.branch32(Assembler::BelowOrEqual, objReg, idRemat.dataReg());
+
+    masm.lshift32(Imm32(3), objReg); /* nargs << 3 == nargs * sizeof(Value) */
+    masm.subPtr(objReg, typeReg); /* fp - numFormalArgs => start of formal args */
+
+    Label loadFromStack = masm.label();
+    masm.move(typeReg, objReg);
+
+    if (idRemat.isConstant()) {
+        Address frameEntry(objReg, v.toInt32() * sizeof(Value));
+        masm.loadValueAsComponents(frameEntry, typeReg, objReg);
+    } else {
+        BaseIndex frameEntry(objReg, idRemat.dataReg(), Assembler::JSVAL_SCALE);
+        masm.loadValueAsComponents(frameEntry, typeReg, objReg);
+    }    
+    Jump done2 = masm.jump();
+
+    notFormalArg.linkTo(masm.label(), &masm);
+
+    masm.push(typeReg);
+
+    Address argsObject(typeReg, StackFrame::offsetOfArgs());
+    masm.loadPtr(argsObject, typeReg);
+
+    masm.load32(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::INITIAL_LENGTH_SLOT)), 
+                typeReg); 
+    masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), typeReg); 
+
+    /* This bascially does fp - (numFormalArgs + numActualArgs + 2) */
+
+    masm.addPtr(typeReg, objReg);
+    masm.addPtr(Imm32(2), objReg);
+    masm.lshiftPtr(Imm32(3), objReg);
+
+    masm.pop(typeReg);
+    masm.subPtr(objReg, typeReg);
+
+    masm.jump(loadFromStack);
+
+    PICLinker buffer(masm, *this);
+
+    if (!buffer.init(cx))
+        return error(cx);
+
+    if (!buffer.verifyRange(cx->fp()->jit()))
+        return disable(cx, "code memory is out of range");
+
+    buffer.link(claspGuard, slowPathStart);
+    buffer.link(overridden, slowPathStart);
+    buffer.link(outOfBounds, slowPathStart);
+    buffer.link(holeCheck, slowPathStart);
+    buffer.link(done, fastPathRejoin);    
+    buffer.link(done2, fastPathRejoin);
+    
+    CodeLocationLabel cs = buffer.finalizeCodeAddendum();
+
+    JaegerSpew(JSpew_PICs, "generated getelem arguments stub at %p\n", cs.executableAddress());
+
+    Repatcher repatcher(cx->fp()->jit());
+    repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), cs);
+
+    JS_ASSERT(!shouldPatchUnconditionalClaspGuard());
+    JS_ASSERT(!inlineClaspGuardPatched);
+
+    inlineClaspGuardPatched = true;
+    stubsGenerated++;
+
+    if (stubsGenerated == MAX_GETELEM_IC_STUBS)
+        disable(cx, "max stubs reached");
+
+    disable(cx, "generated arguments stub");
+
+    if (!obj->getProperty(cx, id, vp))
+        return Lookup_Error;
+
+    return Lookup_Cacheable;
+}
+
 #if defined JS_POLYIC_TYPED_ARRAY
 LookupStatus
 GetElementIC::attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp)
 {
     if (!v.isInt32())
         return disable(cx, "typed array with string key");
 
     if (op == JSOP_CALLELEM)
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -291,16 +291,18 @@ struct GetElementIC : public BasePolyIC 
         inlineClaspGuardPatched = false;
         typeRegHasBaseShape = false;
         hasLastStringStub = false;
     }
     void purge(Repatcher &repatcher);
     LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp);
     LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id,
                                Value *vp);
+    LookupStatus attachArguments(JSContext *cx, JSObject *obj, const Value &v, jsid id,
+                               Value *vp);
     LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id,
                                   Value *vp);
     LookupStatus disable(JSContext *cx, const char *reason);
     LookupStatus error(JSContext *cx);
     bool shouldUpdate(JSContext *cx);
 };
 
 struct SetElementIC : public BaseIC {
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -57,16 +57,17 @@ class ValidateWriter;
 
 namespace js {
 
 #ifdef JS_POLYIC
 struct VMFrame;
 namespace mjit {
 namespace ic {
 struct PICInfo;
+struct GetElementIC;
 
 /* Aargh, Windows. */
 #ifdef GetProp
 #undef GetProp
 #endif
 void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *pic);
 }
 }
@@ -176,16 +177,17 @@ class ArgumentsObject : public ::JSObjec
      * Need access to DATA_SLOT, INITIAL_LENGTH_SLOT, LENGTH_OVERRIDDEN_BIT, and
      * PACKED_BIT_COUNT.
      */
 #ifdef JS_TRACER
     friend class TraceRecorder;
 #endif
 #ifdef JS_POLYIC
     friend class ::GetPropCompiler;
+    friend class mjit::ic::GetElementIC;
 #endif
 
     void setInitialLength(uint32 length);
 
     void setCalleeAndData(JSObject &callee, ArgumentsData *data);
 
   public:
     /* Create an arguments object for the given callee function. */
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1032,16 +1032,20 @@ class StackFrame
     static size_t offsetOfFlags() {
         return offsetof(StackFrame, flags_);
     }
 
     static size_t offsetOfExec() {
         return offsetof(StackFrame, exec);
     }
 
+    static size_t offsetOfArgs() {
+        return offsetof(StackFrame, args);
+    }    
+
     void *addressOfArgs() {
         return &args;
     }
 
     static size_t offsetOfScopeChain() {
         return offsetof(StackFrame, scopeChain_);
     }