IC for JSOP_CALLELEM (bug 604031, r=dmandelin).

This commit is contained in:
David Anderson 2010-10-29 10:28:31 -07:00
Родитель 79ba7ec390
Коммит 86d023b817
8 изменённых файлов: 344 добавлений и 26 удалений

Просмотреть файл

@ -0,0 +1,89 @@
// 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();
}

Просмотреть файл

@ -0,0 +1,93 @@
// 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();
}

Просмотреть файл

@ -0,0 +1,20 @@
// 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");

Просмотреть файл

@ -1189,7 +1189,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_LENGTH)
BEGIN_CASE(JSOP_GETELEM)
if (!jsop_getelem())
if (!jsop_getelem(false))
return Compile_Error;
END_CASE(JSOP_GETELEM)
@ -1766,11 +1766,7 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_UINT24)
BEGIN_CASE(JSOP_CALLELEM)
prepareStubCall(Uses(2));
stubCall(stubs::CallElem);
frame.popn(2);
frame.pushSynced();
frame.pushSynced();
jsop_getelem(true);
END_CASE(JSOP_CALLELEM)
BEGIN_CASE(JSOP_STOP)
@ -4492,3 +4488,13 @@ mjit::Compiler::constructThis()
return true;
}
void
mjit::Compiler::jsop_callelem_slow()
{
prepareStubCall(Uses(2));
stubCall(stubs::CallElem);
frame.popn(2);
frame.pushSynced();
frame.pushSynced();
}

Просмотреть файл

@ -351,6 +351,7 @@ class Compiler : public BaseCompiler
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();
@ -416,7 +417,8 @@ class Compiler : public BaseCompiler
void jsop_arginc(JSOp op, uint32 slot, bool popped);
void jsop_localinc(JSOp op, uint32 slot, bool popped);
void jsop_setelem();
bool jsop_getelem();
bool jsop_getelem(bool isCall);
bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
void jsop_stricteq(JSOp op);
void jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
void jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);

Просмотреть файл

@ -1360,30 +1360,39 @@ mjit::Compiler::jsop_setelem()
stubcc.rejoin(Changes(0));
}
bool
mjit::Compiler::jsop_getelem()
static inline bool
IsCacheableGetElem(FrameEntry *obj, FrameEntry *id)
{
FrameEntry *obj = frame.peek(-2);
FrameEntry *id = frame.peek(-1);
if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT) {
jsop_getelem_slow();
return true;
}
if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT)
return false;
if (id->isTypeKnown() &&
!(id->getKnownType() == JSVAL_TYPE_INT32
#ifdef JS_POLYIC
|| id->getKnownType() == JSVAL_TYPE_STRING
#endif
)) {
jsop_getelem_slow();
return true;
return false;
}
if (id->isTypeKnown() && id->getKnownType() == JSVAL_TYPE_INT32 && id->isConstant() &&
id->getValue().toInt32() < 0) {
jsop_getelem_slow();
return false;
}
return true;
}
bool
mjit::Compiler::jsop_getelem(bool isCall)
{
FrameEntry *obj = frame.peek(-2);
FrameEntry *id = frame.peek(-1);
if (!IsCacheableGetElem(obj, id)) {
if (isCall)
jsop_callelem_slow();
else
jsop_getelem_slow();
return true;
}
@ -1411,6 +1420,14 @@ mjit::Compiler::jsop_getelem()
// 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.
@ -1456,6 +1473,14 @@ mjit::Compiler::jsop_getelem()
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 {
@ -1470,15 +1495,23 @@ mjit::Compiler::jsop_getelem()
objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
#ifdef JS_POLYIC
passICAddress(&ic);
ic.slowPathCall = stubcc.call(ic::GetElement);
if (isCall)
ic.slowPathCall = stubcc.call(ic::CallElement);
else
ic.slowPathCall = stubcc.call(ic::GetElement);
#else
ic.slowPathCall = stubcc.call(stubs::GetElem);
if (isCall)
ic.slowPathCall = stubcc.call(stubs::CallElem);
else
ic.slowPathCall = 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));

Просмотреть файл

@ -50,6 +50,7 @@
#include "jsscopeinlines.h"
#include "jspropertycache.h"
#include "jspropertycacheinlines.h"
#include "jsinterpinlines.h"
#include "jsautooplen.h"
#if defined JS_POLYIC
@ -1944,6 +1945,12 @@ 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)
{
@ -1960,7 +1967,10 @@ LookupStatus
GetElementIC::disable(JSContext *cx, const char *reason)
{
slowCallPatched = true;
BaseIC::disable(cx, reason, JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem));
void *stub = (op == JSOP_GETELEM)
? JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem)
: JS_FUNC_TO_DATA_PTR(void *, DisabledCallElem);
BaseIC::disable(cx, reason, stub);
return Lookup_Uncacheable;
}
@ -2053,6 +2063,13 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
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);
@ -2073,9 +2090,9 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
#if DEBUG
char *chars = js_DeflateString(cx, v.toString()->chars(), v.toString()->length());
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()));
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()));
cx->free(chars);
#endif
@ -2158,6 +2175,63 @@ GetElementIC::update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Valu
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)
{

Просмотреть файл

@ -443,6 +443,7 @@ 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 *);
#endif
} /* namespace ic */