Bug 894596 - Bake constant valued object properties into jitcode when possible, r=jandem, patch mostly written by djvj.

This commit is contained in:
Brian Hackett 2014-08-26 12:30:36 -07:00
parent 6e39acd90d
commit 51f1876d4c
9 changed files with 269 additions and 19 deletions

View File

@ -7542,7 +7542,7 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
#else
bool isTenured = true;
#endif
if (isTenured) {
if (isTenured && tarr->hasSingletonType()) {
// The 'data' pointer can change in rare circumstances
// (ArrayBufferObject::changeContents).
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
@ -8677,6 +8677,10 @@ IonBuilder::jsop_getprop(PropertyName *name)
MDefinition *obj = current->pop();
// Try to optimize to a specific constant.
if (!getPropTryInferredConstant(&emitted, obj, name) || emitted)
return emitted;
// Try to optimize arguments.length.
if (!getPropTryArgumentsLength(&emitted, obj) || emitted)
return emitted;
@ -8766,6 +8770,28 @@ IonBuilder::checkIsDefinitelyOptimizedArguments(MDefinition *obj, bool *isOptimi
return true;
}
bool
IonBuilder::getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name)
{
JS_ASSERT(*emitted == false);
// Need a result typeset to optimize.
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
if (!objTypes)
return true;
Value constVal = UndefinedValue();
if (objTypes->propertyIsConstant(constraints(), NameToId(name), &constVal)) {
spew("Optimized constant property");
obj->setImplicitlyUsedUnchecked();
if (!pushConstant(constVal))
return false;
*emitted = true;
}
return true;
}
bool
IonBuilder::getPropTryArgumentsLength(bool *emitted, MDefinition *obj)
{
@ -9380,13 +9406,16 @@ IonBuilder::jsop_setprop(PropertyName *name)
if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
return emitted;
// Try to emit store from definite slots.
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
return emitted;
// Do not emit optimized stores to slots that may be constant.
if (objTypes && !objTypes->propertyMightBeConstant(constraints(), NameToId(name))) {
// Try to emit store from definite slots.
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
return emitted;
// Try to emit a monomorphic/polymorphic store based on baseline caches.
if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
return emitted;
// Try to emit a monomorphic/polymorphic store based on baseline caches.
if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
return emitted;
}
// Emit a polymorphic cache.
return setPropTryCache(&emitted, obj, name, value, barrier, objTypes);

View File

@ -399,6 +399,7 @@ class IonBuilder : public MIRGenerator
// jsop_getprop() helpers.
bool checkIsDefinitelyOptimizedArguments(MDefinition *obj, bool *isOptimizedArgs);
bool getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name);
bool getPropTryArgumentsLength(bool *emitted, MDefinition *obj);
bool getPropTryArgumentsCallee(bool *emitted, MDefinition *obj, PropertyName *name);
bool getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *name,

View File

@ -1754,6 +1754,69 @@ HeapTypeSetKey::nonWritable(CompilerConstraintList *constraints)
return false;
}
namespace {
class ConstraintDataConstantProperty
{
public:
explicit ConstraintDataConstantProperty() {}
const char *kind() { return "constantProperty"; }
bool invalidateOnNewType(Type type) { return false; }
bool invalidateOnNewPropertyState(TypeSet *property) {
return property->nonConstantProperty();
}
bool invalidateOnNewObjectState(TypeObject *object) { return false; }
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return !invalidateOnNewPropertyState(property.maybeTypes());
}
bool shouldSweep() { return false; }
};
} /* anonymous namespace */
bool
HeapTypeSetKey::constant(CompilerConstraintList *constraints, Value *valOut)
{
if (nonData(constraints))
return false;
if (!maybeTypes())
return false;
if (maybeTypes()->nonConstantProperty())
return false;
// Only singleton object properties can be marked as constants.
JS_ASSERT(object()->singleton());
// Get the current value of the property.
Shape *shape = object()->singleton()->nativeLookupPure(id());
if (!shape)
return false;
Value val = object()->singleton()->nativeGetSlot(shape->slot());
// If the value is a pointer to an object in the nursery, don't optimize.
if (val.isGCThing() && IsInsideNursery(val.toGCThing()))
return false;
// If the value is a string that's not atomic, don't optimize.
if (val.isString() && !val.toString()->isAtom())
return false;
*valOut = val;
LifoAlloc *alloc = constraints->alloc();
typedef CompilerConstraintInstance<ConstraintDataConstantProperty> T;
constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataConstantProperty()));
return true;
}
bool
TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
{
@ -1781,6 +1844,95 @@ TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType)
return true;
}
namespace {
// A constraint that never triggers recompilation.
class ConstraintDataInert
{
public:
explicit ConstraintDataInert() {}
const char *kind() { return "inert"; }
bool invalidateOnNewType(Type type) { return false; }
bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
bool invalidateOnNewObjectState(TypeObject *object) { return false; }
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return true;
}
bool shouldSweep() { return false; }
};
} /* anonymous namespace */
bool
TemporaryTypeSet::propertyMightBeConstant(CompilerConstraintList *constraints, jsid id)
{
if (unknownObject())
return true;
for (size_t i = 0; i < getObjectCount(); i++) {
TypeObjectKey *type = getObject(i);
// Type sets are only marked as constants when they are lazily
// constructed from the properties of singleton typed objects. So watch
// for the cases when a property either already is or might be marked
// as constant in the future.
if (!type || !type->isSingleObject())
continue;
if (type->unknownProperties())
return true;
HeapTypeSetKey property = type->property(id);
if (!property.maybeTypes() || !property.maybeTypes()->nonConstantProperty())
return true;
}
// It is possible for a property that was not marked as constant to
// 'become' one, if we throw away the type property during a GC and
// regenerate it with the constant flag set. TypeObject::sweep only removes
// type properties if they have no constraints attached to them, so add
// inert constraints to pin these properties in place.
LifoAlloc *alloc = constraints->alloc();
for (size_t i = 0; i < getObjectCount(); i++) {
TypeObjectKey *type = getObject(i);
if (!type || !type->isSingleObject())
continue;
HeapTypeSetKey property = type->property(id);
typedef CompilerConstraintInstance<ConstraintDataInert> T;
constraints->add(alloc->new_<T>(alloc, property, ConstraintDataInert()));
}
return false;
}
bool
TemporaryTypeSet::propertyIsConstant(CompilerConstraintList *constraints, jsid id, Value *valOut)
{
JS_ASSERT(valOut);
JSObject *singleton = getSingleton();
if (!singleton)
return false;
TypeObjectKey *type = TypeObjectKey::get(singleton);
if (type->unknownProperties())
return false;
HeapTypeSetKey property = type->property(id);
return property.constant(constraints, valOut);
}
TemporaryTypeSet::DoubleConversion
TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
{
@ -2761,6 +2913,8 @@ static inline void
UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape,
bool indexed)
{
JS_ASSERT(obj->hasSingletonType() && !obj->hasLazyType());
if (!shape->writable())
types->setNonWritableProperty(cx);
@ -2782,6 +2936,14 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
Type type = GetValueType(value);
types->TypeSet::addType(type, &cx->typeLifoAlloc());
}
if (indexed || shape->hadOverwrite()) {
types->setNonConstantProperty(cx);
} else {
InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant",
InferSpewColor(types), types, InferSpewColorReset(),
TypeObjectString(obj->type()), TypeIdString(shape->propid()));
}
}
}
@ -2792,8 +2954,10 @@ TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *t
InferSpewColor(types), types, InferSpewColorReset(),
TypeObjectString(this), TypeIdString(id));
if (!singleton() || !singleton()->isNative())
if (!singleton() || !singleton()->isNative()) {
types->setNonConstantProperty(cx);
return;
}
/*
* Fill the property in with any type the object already has in an own
@ -2895,7 +3059,17 @@ InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
AutoEnterAnalysis enter(cx);
HeapTypeSet *types = obj->getProperty(cx, id);
if (!types || types->hasType(type))
if (!types)
return;
// Clear any constant flag if it exists.
if (!types->nonConstantProperty()) {
InferSpew(ISpewOps, "constantMutated: %sT%p%s %s",
InferSpewColor(types), types, InferSpewColorReset(), TypeString(type));
types->setNonConstantProperty(cx);
}
if (types->hasType(type))
return;
InferSpew(ISpewOps, "externalType: property %s %s: %s",

View File

@ -358,7 +358,7 @@ public:
/*
* For constraints attached to an object property's type set, mark the
* property as having its configuration changed.
* property as having changed somehow.
*/
virtual void newPropertyState(JSContext *cx, TypeSet *source) {}
@ -417,6 +417,9 @@ enum MOZ_ENUM_TYPE(uint32_t) {
/* Whether the property has ever been made non-writable. */
TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,
/* Whether the property might not be constant. */
TYPE_FLAG_NON_CONSTANT_PROPERTY = 0x00020000,
/*
* Whether the property is definitely in a particular slot on all objects
* from which it has not been deleted or reconfigured. For singletons
@ -426,8 +429,8 @@ enum MOZ_ENUM_TYPE(uint32_t) {
* If the property is definite, mask and shift storing the slot + 1.
* Otherwise these bits are clear.
*/
TYPE_FLAG_DEFINITE_MASK = 0xfffe0000,
TYPE_FLAG_DEFINITE_SHIFT = 17
TYPE_FLAG_DEFINITE_MASK = 0xfffc0000,
TYPE_FLAG_DEFINITE_SHIFT = 18
};
typedef uint32_t TypeFlags;
@ -558,6 +561,9 @@ class TypeSet
bool nonWritableProperty() const {
return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY;
}
bool nonConstantProperty() const {
return flags & TYPE_FLAG_NON_CONSTANT_PROPERTY;
}
bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; }
unsigned definiteSlot() const {
JS_ASSERT(definiteProperty());
@ -676,6 +682,9 @@ class HeapTypeSet : public ConstraintTypeSet
/* Mark this type set as representing a non-writable property. */
inline void setNonWritableProperty(ExclusiveContext *cx);
// Mark this type set as being non-constant.
inline void setNonConstantProperty(ExclusiveContext *cx);
};
class CompilerConstraintList;
@ -766,6 +775,10 @@ class TemporaryTypeSet : public TypeSet
/* Whether any objects in the type set needs a barrier on id. */
bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id);
/* Whether any objects in the type set might treat id as a constant property. */
bool propertyMightBeConstant(CompilerConstraintList *constraints, jsid id);
bool propertyIsConstant(CompilerConstraintList *constraints, jsid id, Value *valOut);
/*
* Whether this set contains all types in other, except (possibly) the
* specified type.
@ -1404,6 +1417,7 @@ class HeapTypeSetKey
bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
JSObject *singleton(CompilerConstraintList *constraints);
bool needsBarrier(CompilerConstraintList *constraints);
bool constant(CompilerConstraintList *constraints, Value *valOut);
};
/*

View File

@ -1108,6 +1108,16 @@ HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx)
newPropertyState(cx);
}
inline void
HeapTypeSet::setNonConstantProperty(ExclusiveContext *cx)
{
if (flags & TYPE_FLAG_NON_CONSTANT_PROPERTY)
return;
flags |= TYPE_FLAG_NON_CONSTANT_PROPERTY;
newPropertyState(cx);
}
inline unsigned
TypeSet::getObjectCount() const
{

View File

@ -4056,10 +4056,10 @@ UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType
jsid id = shape->propid();
if (shape->hasSlot()) {
if (mode == ParallelExecution) {
if (!obj->nativeSetSlotIfHasType(shape, value))
if (!obj->nativeSetSlotIfHasType(shape, value, /* overwriting = */ false))
return false;
} else {
obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value);
obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value, /* overwriting = */ false);
}
}
if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {

View File

@ -418,9 +418,10 @@ class JSObject : public js::ObjectImpl
return setSlot(slot, value);
}
inline bool nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value);
inline bool nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value,
bool overwriting = true);
inline void nativeSetSlotWithType(js::ExclusiveContext *cx, js::Shape *shape,
const js::Value &value);
const js::Value &value, bool overwriting = true);
inline const js::Value &getReservedSlot(uint32_t index) const {
JS_ASSERT(index < JSSLOT_FREE(getClass()));

View File

@ -665,19 +665,27 @@ JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
}
inline bool
JSObject::nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value)
JSObject::nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value, bool overwriting)
{
if (!js::types::HasTypePropertyId(this, shape->propid(), value))
return false;
nativeSetSlot(shape->slot(), value);
if (overwriting)
shape->setOverwritten();
return true;
}
inline void
JSObject::nativeSetSlotWithType(js::ExclusiveContext *cx, js::Shape *shape,
const js::Value &value)
const js::Value &value, bool overwriting)
{
nativeSetSlot(shape->slot(), value);
if (overwriting)
shape->setOverwritten();
js::types::AddTypePropertyId(cx, this, shape->propid(), value);
}

View File

@ -823,7 +823,13 @@ class Shape : public gc::BarrieredCell<Shape>
/* Property stored in per-object dictionary, not shared property tree. */
IN_DICTIONARY = 0x02,
UNUSED_BITS = 0x3C
/*
* Slotful property was stored to more than once. This is used as a
* hint for type inference.
*/
OVERWRITTEN = 0x04,
UNUSED_BITS = 0x38
};
/* Get a shape identical to this one, without parent/kids information. */
@ -882,6 +888,13 @@ class Shape : public gc::BarrieredCell<Shape>
: UndefinedValue();
}
void setOverwritten() {
flags |= OVERWRITTEN;
}
bool hadOverwrite() const {
return flags & OVERWRITTEN;
}
void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs);
bool matches(const Shape *other) const {