mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 894596 - Bake constant valued object properties into jitcode when possible, r=jandem, patch mostly written by djvj.
This commit is contained in:
parent
6e39acd90d
commit
51f1876d4c
@ -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,6 +9406,8 @@ IonBuilder::jsop_setprop(PropertyName *name)
|
||||
if (!setPropTryTypedObject(&emitted, obj, name, value) || 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;
|
||||
@ -9387,6 +9415,7 @@ IonBuilder::jsop_setprop(PropertyName *name)
|
||||
// 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);
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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()) {
|
||||
|
@ -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()));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user