зеркало из https://github.com/mozilla/pjs.git
IC for JSOP_CALLELEM (bug 604031, r=dmandelin).
This commit is contained in:
Родитель
79ba7ec390
Коммит
86d023b817
|
@ -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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче