Bug 832364 - Generate optimized paths for element accesses on native objects, r=jandem.

This commit is contained in:
Brian Hackett 2013-01-26 13:21:27 -07:00
parent d5d782111a
commit b6034da246
11 changed files with 242 additions and 68 deletions

View File

@ -2934,14 +2934,31 @@ class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
} }
}; };
bool
CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot)
{
Assembler::Condition cond;
if (index->isConstant())
cond = masm.testMagic(Assembler::Equal, Address(elements, ToInt32(index) * sizeof(js::Value)));
else
cond = masm.testMagic(Assembler::Equal, BaseIndex(elements, ToRegister(index), TimesEight));
return bailoutIf(cond, snapshot);
}
bool bool
CodeGenerator::visitStoreElementT(LStoreElementT *store) CodeGenerator::visitStoreElementT(LStoreElementT *store)
{ {
Register elements = ToRegister(store->elements());
const LAllocation *index = store->index();
if (store->mir()->needsBarrier()) if (store->mir()->needsBarrier())
emitPreBarrier(ToRegister(store->elements()), store->index(), store->mir()->elementType()); emitPreBarrier(elements, index, store->mir()->elementType());
if (store->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, store->snapshot()))
return false;
storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(), storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(),
ToRegister(store->elements()), store->index()); elements, index);
return true; return true;
} }
@ -2950,9 +2967,13 @@ CodeGenerator::visitStoreElementV(LStoreElementV *lir)
{ {
const ValueOperand value = ToValue(lir, LStoreElementV::Value); const ValueOperand value = ToValue(lir, LStoreElementV::Value);
Register elements = ToRegister(lir->elements()); Register elements = ToRegister(lir->elements());
const LAllocation *index = lir->index();
if (lir->mir()->needsBarrier()) if (lir->mir()->needsBarrier())
emitPreBarrier(elements, lir->index(), MIRType_Value); emitPreBarrier(elements, index, MIRType_Value);
if (lir->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, lir->snapshot()))
return false;
if (lir->index()->isConstant()) if (lir->index()->isConstant())
masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value))); masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));

View File

@ -253,6 +253,9 @@ class CodeGenerator : public CodeGeneratorSpecific
// be tested in the first place.) // be tested in the first place.)
void testObjectTruthy(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch, void testObjectTruthy(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch,
OutOfLineTestObject *ool); OutOfLineTestObject *ool);
// Bailout if an element about to be written to is a hole.
bool emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot);
}; };
} // namespace ion } // namespace ion

View File

@ -4324,7 +4324,7 @@ IonBuilder::jsop_initelem_array()
current->add(elements); current->add(elements);
// Store the value. // Store the value.
MStoreElement *store = MStoreElement::New(elements, id, value); MStoreElement *store = MStoreElement::New(elements, id, value, /* needsHoleCheck = */ false);
current->add(store); current->add(store);
// Update the length. // Update the length.
@ -5214,7 +5214,7 @@ IonBuilder::jsop_getelem()
{ {
RootedScript script(cx, this->script()); RootedScript script(cx, this->script());
if (oracle->elementReadIsDenseArray(script, pc)) if (oracle->elementReadIsDenseNative(script, pc))
return jsop_getelem_dense(); return jsop_getelem_dense();
int arrayType = TypedArray::TYPE_MAX; int arrayType = TypedArray::TYPE_MAX;
@ -5266,14 +5266,17 @@ IonBuilder::jsop_getelem()
bool bool
IonBuilder::jsop_getelem_dense() IonBuilder::jsop_getelem_dense()
{ {
if (oracle->arrayPrototypeHasIndexedProperty())
return abort("GETELEM Array proto has indexed properties");
RootedScript scriptRoot(cx, script()); RootedScript scriptRoot(cx, script());
types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc); types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
types::StackTypeSet *types = oracle->propertyRead(script(), pc); types::StackTypeSet *types = oracle->propertyRead(script(), pc);
bool needsHoleCheck = !oracle->elementReadIsPacked(script(), pc); bool needsHoleCheck = !oracle->elementReadIsPacked(script(), pc);
bool maybeUndefined = types->hasType(types::Type::UndefinedType());
// Reads which are on holes in the object do not have to bail out if
// undefined values have been observed at this access site and the access
// cannot hit another indexed property on the object or its prototypes.
bool readOutOfBounds =
types->hasType(types::Type::UndefinedType()) &&
!oracle->elementReadHasExtraIndexedProperty(script(), pc);
MDefinition *id = current->pop(); MDefinition *id = current->pop();
MDefinition *obj = current->pop(); MDefinition *obj = current->pop();
@ -5310,7 +5313,7 @@ IonBuilder::jsop_getelem_dense()
MInstruction *load; MInstruction *load;
if (!maybeUndefined) { if (!readOutOfBounds) {
// This load should not return undefined, so likely we're reading // This load should not return undefined, so likely we're reading
// in-bounds elements, and the array is packed or its holes are not // in-bounds elements, and the array is packed or its holes are not
// read. This is the best case: we can separate the bounds check for // read. This is the best case: we can separate the bounds check for
@ -5483,7 +5486,7 @@ IonBuilder::jsop_setelem()
RootedScript script(cx, this->script()); RootedScript script(cx, this->script());
if (oracle->propertyWriteCanSpecialize(script, pc)) { if (oracle->propertyWriteCanSpecialize(script, pc)) {
if (oracle->elementWriteIsDenseArray(script, pc)) if (oracle->elementWriteIsDenseNative(script, pc))
return jsop_setelem_dense(); return jsop_setelem_dense();
int arrayType = TypedArray::TYPE_MAX; int arrayType = TypedArray::TYPE_MAX;
@ -5511,12 +5514,13 @@ IonBuilder::jsop_setelem()
bool bool
IonBuilder::jsop_setelem_dense() IonBuilder::jsop_setelem_dense()
{ {
if (oracle->arrayPrototypeHasIndexedProperty())
return abort("SETELEM Array proto has indexed properties");
MIRType elementType = oracle->elementWrite(script(), pc); MIRType elementType = oracle->elementWrite(script(), pc);
bool packed = oracle->elementWriteIsPacked(script(), pc); bool packed = oracle->elementWriteIsPacked(script(), pc);
// Writes which are on holes in the object do not have to bail out if they
// cannot hit another indexed property on the object or its prototypes.
bool writeOutOfBounds = !oracle->elementWriteHasExtraIndexedProperty(script(), pc);
MDefinition *value = current->pop(); MDefinition *value = current->pop();
MDefinition *id = current->pop(); MDefinition *id = current->pop();
MDefinition *obj = current->pop(); MDefinition *obj = current->pop();
@ -5534,7 +5538,7 @@ IonBuilder::jsop_setelem_dense()
// indexes in the past. Otherwise, use MStoreElement so that we can hoist // indexes in the past. Otherwise, use MStoreElement so that we can hoist
// the initialized length and bounds check. // the initialized length and bounds check.
MStoreElementCommon *store; MStoreElementCommon *store;
if (oracle->setElementHasWrittenHoles(script(), pc)) { if (oracle->setElementHasWrittenHoles(script(), pc) && writeOutOfBounds) {
MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, value); MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, value);
store = ins; store = ins;
@ -5549,7 +5553,9 @@ IonBuilder::jsop_setelem_dense()
id = addBoundsCheck(id, initLength); id = addBoundsCheck(id, initLength);
MStoreElement *ins = MStoreElement::New(elements, id, value); bool needsHoleCheck = !packed && !writeOutOfBounds;
MStoreElement *ins = MStoreElement::New(elements, id, value, needsHoleCheck);
store = ins; store = ins;
current->add(ins); current->add(ins);
@ -6794,7 +6800,7 @@ bool
IonBuilder::jsop_in() IonBuilder::jsop_in()
{ {
RootedScript scriptRoot(cx, script()); RootedScript scriptRoot(cx, script());
if (oracle->inObjectIsDenseArray(scriptRoot, pc)) if (oracle->inObjectIsDenseNativeWithoutExtraIndexedProperties(scriptRoot, pc))
return jsop_in_dense(); return jsop_in_dense();
MDefinition *obj = current->pop(); MDefinition *obj = current->pop();
@ -6810,9 +6816,6 @@ IonBuilder::jsop_in()
bool bool
IonBuilder::jsop_in_dense() IonBuilder::jsop_in_dense()
{ {
if (oracle->arrayPrototypeHasIndexedProperty())
return abort("JSOP_IN Array proto has indexed properties");
bool needsHoleCheck = !oracle->inArrayIsPacked(script(), pc); bool needsHoleCheck = !oracle->inArrayIsPacked(script(), pc);
MDefinition *obj = current->pop(); MDefinition *obj = current->pop();

View File

@ -1655,6 +1655,8 @@ LIRGenerator::visitStoreElement(MStoreElement *ins)
case MIRType_Value: case MIRType_Value:
{ {
LInstruction *lir = new LStoreElementV(elements, index); LInstruction *lir = new LStoreElementV(elements, index);
if (ins->fallible() && !assignSnapshot(lir))
return false;
if (!useBox(lir, LStoreElementV::Value, ins->value())) if (!useBox(lir, LStoreElementV::Value, ins->value()))
return false; return false;
return add(lir, ins); return add(lir, ins);
@ -1663,7 +1665,10 @@ LIRGenerator::visitStoreElement(MStoreElement *ins)
default: default:
{ {
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
return add(new LStoreElementT(elements, index, value), ins); LInstruction *lir = new LStoreElementT(elements, index, value);
if (ins->fallible() && !assignSnapshot(lir))
return false;
return add(lir, ins);
} }
} }
} }

View File

@ -222,7 +222,8 @@ IonBuilder::inlineArray(uint32_t argc, bool constructing)
id = MConstant::New(Int32Value(i)); id = MConstant::New(Int32Value(i));
current->add(id); current->add(id);
MStoreElement *store = MStoreElement::New(elements, id, argv[i + 1]); MStoreElement *store = MStoreElement::New(elements, id, argv[i + 1],
/* needsHoleCheck = */ false);
current->add(store); current->add(store);
} }

View File

@ -3852,10 +3852,13 @@ class MStoreElement
public MStoreElementCommon, public MStoreElementCommon,
public SingleObjectPolicy public SingleObjectPolicy
{ {
MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value) { bool needsHoleCheck_;
MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
initOperand(0, elements); initOperand(0, elements);
initOperand(1, index); initOperand(1, index);
initOperand(2, value); initOperand(2, value);
needsHoleCheck_ = needsHoleCheck;
JS_ASSERT(elements->type() == MIRType_Elements); JS_ASSERT(elements->type() == MIRType_Elements);
JS_ASSERT(index->type() == MIRType_Int32); JS_ASSERT(index->type() == MIRType_Int32);
} }
@ -3863,8 +3866,9 @@ class MStoreElement
public: public:
INSTRUCTION_HEADER(StoreElement) INSTRUCTION_HEADER(StoreElement)
static MStoreElement *New(MDefinition *elements, MDefinition *index, MDefinition *value) { static MStoreElement *New(MDefinition *elements, MDefinition *index, MDefinition *value,
return new MStoreElement(elements, index, value); bool needsHoleCheck) {
return new MStoreElement(elements, index, value, needsHoleCheck);
} }
MDefinition *elements() const { MDefinition *elements() const {
return getOperand(0); return getOperand(0);
@ -3881,6 +3885,12 @@ class MStoreElement
AliasSet getAliasSet() const { AliasSet getAliasSet() const {
return AliasSet::Store(AliasSet::Element); return AliasSet::Store(AliasSet::Element);
} }
bool needsHoleCheck() const {
return needsHoleCheck_;
}
bool fallible() const {
return needsHoleCheck();
}
}; };
// Like MStoreElement, but supports indexes >= initialized length. The downside // Like MStoreElement, but supports indexes >= initialized length. The downside

View File

@ -263,9 +263,9 @@ TypeInferenceOracle::propertyReadAccessGetter(UnrootedScript script, jsbytecode
} }
bool bool
TypeInferenceOracle::inObjectIsDenseArray(HandleScript script, jsbytecode *pc) TypeInferenceOracle::inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc)
{ {
// Check whether the object is a dense array and index is int32 or double. // Check whether the object is a native and index is int32 or double.
StackTypeSet *id = script->analysis()->poppedTypes(pc, 1); StackTypeSet *id = script->analysis()->poppedTypes(pc, 1);
StackTypeSet *obj = script->analysis()->poppedTypes(pc, 0); StackTypeSet *obj = script->analysis()->poppedTypes(pc, 0);
@ -273,7 +273,11 @@ TypeInferenceOracle::inObjectIsDenseArray(HandleScript script, jsbytecode *pc)
if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
return false; return false;
return obj->getKnownClass() == &ArrayClass; Class *clasp = obj->getKnownClass();
if (!clasp || !clasp->isNative())
return false;
return !types::TypeCanHaveExtraIndexedProperties(cx, obj);
} }
bool bool
@ -284,7 +288,7 @@ TypeInferenceOracle::inArrayIsPacked(UnrootedScript script, jsbytecode *pc)
} }
bool bool
TypeInferenceOracle::elementReadIsDenseArray(RawScript script, jsbytecode *pc) TypeInferenceOracle::elementReadIsDenseNative(RawScript script, jsbytecode *pc)
{ {
// Check whether the object is a dense array and index is int32 or double. // Check whether the object is a dense array and index is int32 or double.
StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1); StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
@ -294,11 +298,8 @@ TypeInferenceOracle::elementReadIsDenseArray(RawScript script, jsbytecode *pc)
if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
return false; return false;
if (obj->getKnownClass() != &ArrayClass) Class *clasp = obj->getKnownClass();
return false; return clasp && clasp->isNative();
return !obj->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
types::OBJECT_FLAG_LENGTH_OVERFLOW);
} }
bool bool
@ -358,6 +359,13 @@ TypeInferenceOracle::elementReadIsString(UnrootedScript script, jsbytecode *pc)
return true; return true;
} }
bool
TypeInferenceOracle::elementReadHasExtraIndexedProperty(UnrootedScript script, jsbytecode *pc)
{
StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
return types::TypeCanHaveExtraIndexedProperties(cx, obj);
}
bool bool
TypeInferenceOracle::elementReadIsPacked(UnrootedScript script, jsbytecode *pc) TypeInferenceOracle::elementReadIsPacked(UnrootedScript script, jsbytecode *pc)
{ {
@ -386,7 +394,7 @@ TypeInferenceOracle::elementReadGeneric(UnrootedScript script, jsbytecode *pc, b
} }
bool bool
TypeInferenceOracle::elementWriteIsDenseArray(HandleScript script, jsbytecode *pc) TypeInferenceOracle::elementWriteIsDenseNative(HandleScript script, jsbytecode *pc)
{ {
// Check whether the object is a dense array and index is int32 or double. // Check whether the object is a dense array and index is int32 or double.
StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2); StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
@ -396,11 +404,8 @@ TypeInferenceOracle::elementWriteIsDenseArray(HandleScript script, jsbytecode *p
if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE) if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
return false; return false;
if (obj->getKnownClass() != &ArrayClass) Class *clasp = obj->getKnownClass();
return false; return clasp && clasp->isNative();
return !obj->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES |
types::OBJECT_FLAG_LENGTH_OVERFLOW);
} }
bool bool
@ -421,6 +426,17 @@ TypeInferenceOracle::elementWriteIsTypedArray(RawScript script, jsbytecode *pc,
return true; return true;
} }
bool
TypeInferenceOracle::elementWriteHasExtraIndexedProperty(UnrootedScript script, jsbytecode *pc)
{
StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
if (obj->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
return true;
return types::TypeCanHaveExtraIndexedProperties(cx, obj);
}
bool bool
TypeInferenceOracle::elementWriteIsPacked(UnrootedScript script, jsbytecode *pc) TypeInferenceOracle::elementWriteIsPacked(UnrootedScript script, jsbytecode *pc)
{ {
@ -446,6 +462,9 @@ TypeInferenceOracle::elementWrite(UnrootedScript script, jsbytecode *pc)
return MIRType_None; return MIRType_None;
if (TypeObject *object = objTypes->getTypeObject(i)) { if (TypeObject *object = objTypes->getTypeObject(i)) {
if (object->unknownProperties())
return MIRType_None;
types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false); types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
if (!elementTypes) if (!elementTypes)
return MIRType_None; return MIRType_None;
@ -464,13 +483,6 @@ TypeInferenceOracle::elementWrite(UnrootedScript script, jsbytecode *pc)
return elementType; return elementType;
} }
bool
TypeInferenceOracle::arrayPrototypeHasIndexedProperty()
{
RootedScript scriptRoot(cx, script());
return ArrayPrototypeHasIndexedProperty(cx, scriptRoot);
}
bool bool
TypeInferenceOracle::canInlineCalls() TypeInferenceOracle::canInlineCalls()
{ {

View File

@ -75,13 +75,13 @@ class TypeOracle
*barrier = NULL; *barrier = NULL;
return NULL; return NULL;
} }
virtual bool inObjectIsDenseArray(HandleScript script, jsbytecode *pc) { virtual bool inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc) {
return false; return false;
} }
virtual bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc) { virtual bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc) {
return false; return false;
} }
virtual bool elementReadIsDenseArray(RawScript script, jsbytecode *pc) { virtual bool elementReadIsDenseNative(RawScript script, jsbytecode *pc) {
return false; return false;
} }
virtual bool elementReadIsTypedArray(HandleScript script, jsbytecode *pc, int *arrayType) { virtual bool elementReadIsTypedArray(HandleScript script, jsbytecode *pc, int *arrayType) {
@ -90,6 +90,9 @@ class TypeOracle
virtual bool elementReadIsString(UnrootedScript script, jsbytecode *pc) { virtual bool elementReadIsString(UnrootedScript script, jsbytecode *pc) {
return false; return false;
} }
virtual bool elementReadHasExtraIndexedProperty(UnrootedScript, jsbytecode *pc) {
return false;
}
virtual bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc) { virtual bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc) {
return false; return false;
} }
@ -100,12 +103,15 @@ class TypeOracle
virtual bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc) { virtual bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc) {
return true; return true;
} }
virtual bool elementWriteIsDenseArray(HandleScript script, jsbytecode *pc) { virtual bool elementWriteIsDenseNative(HandleScript script, jsbytecode *pc) {
return false; return false;
} }
virtual bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) { virtual bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType) {
return false; return false;
} }
virtual bool elementWriteHasExtraIndexedProperty(UnrootedScript script, jsbytecode *pc) {
return false;
}
virtual bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc) { virtual bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc) {
return false; return false;
} }
@ -121,9 +127,6 @@ class TypeOracle
virtual MIRType elementWrite(UnrootedScript script, jsbytecode *pc) { virtual MIRType elementWrite(UnrootedScript script, jsbytecode *pc) {
return MIRType_None; return MIRType_None;
} }
virtual bool arrayPrototypeHasIndexedProperty() {
return true;
}
virtual bool canInlineCalls() { virtual bool canInlineCalls() {
return false; return false;
} }
@ -229,22 +232,23 @@ class TypeInferenceOracle : public TypeOracle
types::StackTypeSet *getCallTarget(UnrootedScript caller, uint32_t argc, jsbytecode *pc); types::StackTypeSet *getCallTarget(UnrootedScript caller, uint32_t argc, jsbytecode *pc);
types::StackTypeSet *getCallArg(UnrootedScript caller, uint32_t argc, uint32_t arg, jsbytecode *pc); types::StackTypeSet *getCallArg(UnrootedScript caller, uint32_t argc, uint32_t arg, jsbytecode *pc);
types::StackTypeSet *getCallReturn(UnrootedScript caller, jsbytecode *pc); types::StackTypeSet *getCallReturn(UnrootedScript caller, jsbytecode *pc);
bool inObjectIsDenseArray(HandleScript script, jsbytecode *pc); bool inObjectIsDenseNativeWithoutExtraIndexedProperties(HandleScript script, jsbytecode *pc);
bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc); bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc);
bool elementReadIsDenseArray(RawScript script, jsbytecode *pc); bool elementReadIsDenseNative(RawScript script, jsbytecode *pc);
bool elementReadIsTypedArray(HandleScript script, jsbytecode *pc, int *atype); bool elementReadIsTypedArray(HandleScript script, jsbytecode *pc, int *atype);
bool elementReadIsString(UnrootedScript script, jsbytecode *pc); bool elementReadIsString(UnrootedScript script, jsbytecode *pc);
bool elementReadHasExtraIndexedProperty(UnrootedScript, jsbytecode *pc);
bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc); bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc);
void elementReadGeneric(UnrootedScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult); void elementReadGeneric(UnrootedScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult);
bool elementWriteIsDenseArray(HandleScript script, jsbytecode *pc); bool elementWriteIsDenseNative(HandleScript script, jsbytecode *pc);
bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType); bool elementWriteIsTypedArray(RawScript script, jsbytecode *pc, int *arrayType);
bool elementWriteHasExtraIndexedProperty(UnrootedScript script, jsbytecode *pc);
bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc); bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc);
bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc); bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc);
bool propertyWriteCanSpecialize(UnrootedScript script, jsbytecode *pc); bool propertyWriteCanSpecialize(UnrootedScript script, jsbytecode *pc);
bool propertyWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc, RawId id); bool propertyWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc, RawId id);
bool elementWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc); bool elementWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc);
MIRType elementWrite(UnrootedScript script, jsbytecode *pc); MIRType elementWrite(UnrootedScript script, jsbytecode *pc);
bool arrayPrototypeHasIndexedProperty();
bool canInlineCalls(); bool canInlineCalls();
bool canInlineCall(HandleScript caller, jsbytecode *pc); bool canInlineCall(HandleScript caller, jsbytecode *pc);
bool canEnterInlinedFunction(JSFunction *callee); bool canEnterInlinedFunction(JSFunction *callee);

View File

@ -0,0 +1,44 @@
function testOverwritingSparseHole()
{
var x = [];
for (var i = 0; i < 50; i++)
x[i] = i;
var hit = false;
Object.defineProperty(x, 40, {set: function() { hit = true; }});
for (var i = 0; i < 50; i++)
x[i] = 10;
assertEq(hit, true);
}
testOverwritingSparseHole();
function testReadingSparseHole()
{
var x = [];
for (var i = 5; i < 50; i++)
x[i] = i;
var hit = false;
Object.defineProperty(x, 40, {get: function() { hit = true; return 5.5; }});
var res = 0;
for (var i = 0; i < 50; i++) {
res += x[i];
if (i == 10)
res = 0;
}
assertEq(res, 1135.5);
assertEq(hit, true);
}
testReadingSparseHole();
function testInSparseHole()
{
var x = [];
for (var i = 5; i < 50; i++)
x[i] = i;
Object.defineProperty(x, 40, {get: function() {}});
var res = 0;
for (var i = 0; i < 50; i++)
res += (i in x) ? 1 : 0;
assertEq(res, 45);
}
testInSparseHole();

View File

@ -2037,6 +2037,37 @@ StackTypeSet::isDOMClass()
return true; return true;
} }
JSObject *
StackTypeSet::getCommonPrototype()
{
if (unknownObject())
return NULL;
JSObject *proto = NULL;
unsigned count = getObjectCount();
for (unsigned i = 0; i < count; i++) {
TaggedProto nproto;
if (RawObject object = getSingleObject(i))
nproto = object->getProto();
else if (TypeObject *object = getTypeObject(i))
nproto = object->proto.get();
else
continue;
if (proto) {
if (nproto != proto)
return NULL;
} else {
if (!nproto.isObject())
return NULL;
proto = nproto.toObject();
}
}
return proto;
}
JSObject * JSObject *
StackTypeSet::getSingleton() StackTypeSet::getSingleton()
{ {
@ -2483,27 +2514,60 @@ types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc,
return !script->analysis()->getCode(pc).inLoop; return !script->analysis()->getCode(pc).inLoop;
} }
static inline bool
ClassCanHaveExtraProperties(Class *clasp)
{
JS_ASSERT(clasp->resolve);
return clasp->resolve != JS_ResolveStub || clasp->ops.lookupGeneric || clasp->ops.getGeneric;
}
static inline bool
PrototypeHasIndexedProperty(JSContext *cx, JSObject *obj)
{
do {
TypeObject *type = obj->getType(cx);
if (ClassCanHaveExtraProperties(type->clasp))
return true;
if (type->unknownProperties())
return true;
HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx))
return true;
obj = obj->getProto();
} while (obj);
return false;
}
bool bool
types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script) types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script)
{ {
if (!cx->typeInferenceEnabled() || !script->compileAndGo) if (!cx->typeInferenceEnabled() || !script->compileAndGo)
return true; return true;
RootedObject proto(cx, script->global().getOrCreateArrayPrototype(cx)); JSObject *proto = script->global().getOrCreateArrayPrototype(cx);
if (!proto) if (!proto)
return true; return true;
do { return PrototypeHasIndexedProperty(cx, proto);
TypeObject *type = proto->getType(cx); }
if (type->unknownProperties())
return true;
HeapTypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx))
return true;
proto = proto->getProto();
} while (proto);
return false; bool
types::TypeCanHaveExtraIndexedProperties(JSContext *cx, StackTypeSet *types)
{
Class *clasp = types->getKnownClass();
if (!clasp || ClassCanHaveExtraProperties(clasp))
return true;
if (types->hasObjectFlags(cx, types::OBJECT_FLAG_SPARSE_INDEXES))
return true;
JSObject *proto = types->getCommonPrototype();
if (!proto)
return true;
return PrototypeHasIndexedProperty(cx, proto);
} }
bool bool

View File

@ -592,6 +592,9 @@ class StackTypeSet : public TypeSet
/* Get the class shared by all objects in this set, or NULL. */ /* Get the class shared by all objects in this set, or NULL. */
Class *getKnownClass(); Class *getKnownClass();
/* Get the prototype shared by all objects in this set, or NULL. */
JSObject *getCommonPrototype();
/* Get the typed array type of all objects in this set, or TypedArray::TYPE_MAX. */ /* Get the typed array type of all objects in this set, or TypedArray::TYPE_MAX. */
int getTypedArrayType(); int getTypedArrayType();
@ -1101,6 +1104,10 @@ UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, JSProt
bool bool
ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script); ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script);
/* Whether obj or any of its prototypes have an indexed property. */
bool
TypeCanHaveExtraIndexedProperties(JSContext *cx, StackTypeSet *types);
/* /*
* Type information about a callsite. this is separated from the bytecode * Type information about a callsite. this is separated from the bytecode
* information itself so we can handle higher order functions not called * information itself so we can handle higher order functions not called