mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 832364 - Generate optimized paths for element accesses on native objects, r=jandem.
This commit is contained in:
parent
d5d782111a
commit
b6034da246
@ -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)));
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
44
js/src/jit-test/tests/ion/nativeElementAccesses.js
Normal file
44
js/src/jit-test/tests/ion/nativeElementAccesses.js
Normal 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();
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user