зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1131531 - Ion GetElement IC should handle dense element holes. r=jandem
This commit is contained in:
Родитель
5189e1f281
Коммит
affc0dee90
|
@ -0,0 +1,18 @@
|
|||
function f(obj) {
|
||||
return typeof obj[15];
|
||||
}
|
||||
|
||||
function test() {
|
||||
var a = [1, 2];
|
||||
a.__proto__ = {15: 1337};
|
||||
var b = [1, 2, 3, 4];
|
||||
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var r = f(i % 2 ? a : b);
|
||||
assertEq(r, i % 2 ? "number" : "undefined");
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
test();
|
||||
test();
|
|
@ -0,0 +1,28 @@
|
|||
function f() {
|
||||
var x = [1, 2, 3];
|
||||
var y = {};
|
||||
x.__proto__ = y;
|
||||
|
||||
for (var i = 0; i < 200; i++) {
|
||||
if (i == 100)
|
||||
y[100000] = 15;
|
||||
else
|
||||
assertEq(typeof x[100000], i > 100 ? "number" : "undefined");
|
||||
}
|
||||
}
|
||||
|
||||
function g() {
|
||||
var x = [1, 2, 3];
|
||||
var y = {};
|
||||
x.__proto__ = y;
|
||||
|
||||
for (var i = 0; i < 200; i++) {
|
||||
if (i == 100)
|
||||
y[4] = 15;
|
||||
else
|
||||
assertEq(typeof x[4], i > 100 ? "number" : "undefined");
|
||||
}
|
||||
}
|
||||
|
||||
f();
|
||||
g();
|
|
@ -3258,6 +3258,160 @@ GetElementIC::attachDenseElement(JSContext *cx, HandleScript outerScript, IonScr
|
|||
return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
|
||||
}
|
||||
|
||||
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachDenseElementHole(JSObject *obj, const Value &idval, TypedOrValueRegister output)
|
||||
{
|
||||
if (!idval.isInt32())
|
||||
return false;
|
||||
|
||||
if (!output.hasValue())
|
||||
return false;
|
||||
|
||||
if (!obj->isNative())
|
||||
return false;
|
||||
|
||||
if (obj->as<NativeObject>().getDenseInitializedLength() == 0)
|
||||
return false;
|
||||
|
||||
while (obj) {
|
||||
if (obj->isIndexed())
|
||||
return false;
|
||||
|
||||
if (ClassCanHaveExtraProperties(obj->getClass()))
|
||||
return false;
|
||||
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto)
|
||||
break;
|
||||
|
||||
if (!proto->isNative())
|
||||
return false;
|
||||
|
||||
// Make sure objects on the prototype don't have dense elements.
|
||||
if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
|
||||
return false;
|
||||
|
||||
obj = proto;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateDenseElementHole(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
IonScript *ion, JSObject *obj, const Value &idval,
|
||||
Register object, ConstantOrRegister index, TypedOrValueRegister output)
|
||||
{
|
||||
MOZ_ASSERT(GetElementIC::canAttachDenseElementHole(obj, idval, output));
|
||||
MOZ_ASSERT(obj->lastProperty());
|
||||
|
||||
Register scratchReg = output.valueReg().scratchReg();
|
||||
|
||||
// Guard on the shape and group, to prevent non-dense elements from appearing.
|
||||
Label failures;
|
||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||
Address(object, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(obj->lastProperty()), &failures);
|
||||
|
||||
|
||||
if (obj->hasUncacheableProto()) {
|
||||
masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg);
|
||||
Address proto(scratchReg, ObjectGroup::offsetOfProto());
|
||||
masm.branchPtr(Assembler::NotEqual, proto,
|
||||
ImmMaybeNurseryPtr(obj->getProto()), &failures);
|
||||
}
|
||||
|
||||
JSObject *pobj = obj->getProto();
|
||||
while (pobj) {
|
||||
MOZ_ASSERT(pobj->lastProperty());
|
||||
|
||||
masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
|
||||
if (pobj->hasUncacheableProto()) {
|
||||
MOZ_ASSERT(!pobj->isSingleton());
|
||||
Address groupAddr(scratchReg, JSObject::offsetOfGroup());
|
||||
masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), &failures);
|
||||
}
|
||||
|
||||
// Make sure the shape matches, to avoid non-dense elements.
|
||||
masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(pobj->lastProperty()), &failures);
|
||||
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(scratchReg, NativeObject::offsetOfElements()), scratchReg);
|
||||
|
||||
// Also make sure there are no dense elements.
|
||||
Label hole;
|
||||
Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::NotEqual, initLength, Imm32(0), &failures);
|
||||
|
||||
pobj = pobj->getProto();
|
||||
}
|
||||
|
||||
// Ensure the index is an int32 value.
|
||||
Register indexReg = InvalidReg;
|
||||
Register elementsReg = InvalidReg;
|
||||
|
||||
if (index.reg().hasValue()) {
|
||||
indexReg = scratchReg;
|
||||
MOZ_ASSERT(indexReg != InvalidReg);
|
||||
ValueOperand val = index.reg().valueReg();
|
||||
|
||||
masm.branchTestInt32(Assembler::NotEqual, val, &failures);
|
||||
|
||||
// Unbox the index.
|
||||
masm.unboxInt32(val, indexReg);
|
||||
|
||||
// Save the object register.
|
||||
masm.push(object);
|
||||
elementsReg = object;
|
||||
} else {
|
||||
MOZ_ASSERT(!index.reg().typedReg().isFloat());
|
||||
indexReg = index.reg().typedReg().gpr();
|
||||
elementsReg = scratchReg;
|
||||
}
|
||||
|
||||
// Load elements vector.
|
||||
masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elementsReg);
|
||||
|
||||
// Guard on the initialized length.
|
||||
Label hole;
|
||||
Address initLength(elementsReg, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
|
||||
|
||||
// Load the value.
|
||||
Label done;
|
||||
masm.loadValue(BaseObjectElementIndex(elementsReg, indexReg), output.valueReg());
|
||||
masm.branchTestMagic(Assembler::NotEqual, output.valueReg(), &done);
|
||||
|
||||
// Load undefined for the hole.
|
||||
masm.bind(&hole);
|
||||
masm.moveValue(UndefinedValue(), output.valueReg());
|
||||
|
||||
masm.bind(&done);
|
||||
// Restore the object register.
|
||||
if (elementsReg == object)
|
||||
masm.pop(object);
|
||||
attacher.jumpRejoin(masm);
|
||||
|
||||
// All failure flows through here.
|
||||
masm.bind(&failures);
|
||||
attacher.jumpNextStub(masm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetElementIC::attachDenseElementHole(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, const Value &idval)
|
||||
{
|
||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
||||
RepatchStubAppender attacher(*this);
|
||||
GenerateDenseElementHole(cx, masm, attacher, ion, obj, idval, object(), index(), output());
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "dense hole");
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
|
||||
TypedOrValueRegister output)
|
||||
|
@ -3568,6 +3722,13 @@ GetElementIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex,
|
|||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && cache.monitoredResult() &&
|
||||
canAttachDenseElementHole(obj, idval, cache.output()))
|
||||
{
|
||||
if (!cache.attachDenseElementHole(cx, outerScript, ion, obj, idval))
|
||||
return false;
|
||||
attachedStub = true;
|
||||
}
|
||||
if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
|
||||
if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
|
||||
return false;
|
||||
|
|
|
@ -842,6 +842,8 @@ class GetElementIC : public RepatchIonCache
|
|||
|
||||
static bool canAttachGetProp(JSObject *obj, const Value &idval, jsid id);
|
||||
static bool canAttachDenseElement(JSObject *obj, const Value &idval);
|
||||
static bool canAttachDenseElementHole(JSObject *obj, const Value &idval,
|
||||
TypedOrValueRegister output);
|
||||
static bool canAttachTypedArrayElement(JSObject *obj, const Value &idval,
|
||||
TypedOrValueRegister output);
|
||||
|
||||
|
@ -851,6 +853,9 @@ class GetElementIC : public RepatchIonCache
|
|||
bool attachDenseElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, const Value &idval);
|
||||
|
||||
bool attachDenseElementHole(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject obj, const Value &idval);
|
||||
|
||||
bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
|
||||
HandleObject tarr, const Value &idval);
|
||||
|
||||
|
|
|
@ -2268,8 +2268,8 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ClassCanHaveExtraProperties(const Class *clasp)
|
||||
bool
|
||||
js::ClassCanHaveExtraProperties(const Class *clasp)
|
||||
{
|
||||
return clasp->resolve
|
||||
|| clasp->ops.lookupProperty
|
||||
|
|
|
@ -909,6 +909,9 @@ class TypeNewScript
|
|||
/* Is this a reasonable PC to be doing inlining on? */
|
||||
inline bool isInlinableCall(jsbytecode *pc);
|
||||
|
||||
bool
|
||||
ClassCanHaveExtraProperties(const Class *clasp);
|
||||
|
||||
/*
|
||||
* Whether Array.prototype, or an object on its proto chain, has an
|
||||
* indexed property.
|
||||
|
|
Загрузка…
Ссылка в новой задаче