Bug 924611 - Don't create lazy type objects and type properties in IonBuilder, r=jandem.

This commit is contained in:
Brian Hackett 2013-10-14 12:13:41 -06:00
parent 251cbba440
commit 724613f48e
12 changed files with 285 additions and 161 deletions

View File

@ -1368,9 +1368,6 @@ ICUpdatedStub::addUpdateStubForValue(JSContext *cx, HandleScript script, HandleO
return true;
}
if (!obj->getType(cx))
return false;
types::EnsureTrackPropertyTypes(cx, obj, id);
if (val.isPrimitive()) {
@ -5326,6 +5323,10 @@ TryAttachGlobalNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *
RootedId id(cx, NameToId(name));
// Instantiate this global property, for use during Ion compilation.
if (IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, global, NameToId(name));
// The property must be found, and it must be found as a normal data property.
RootedShape shape(cx, global->nativeLookup(cx, id));
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
@ -5383,6 +5384,10 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s
if (!IsCacheableGetPropReadSlot(scopeChain, scopeChain, shape))
return true;
// Instantiate properties on singleton scope chain objects, for use during Ion compilation.
if (scopeChain->hasSingletonType() && IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, scopeChain, NameToId(name));
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(scopeChain, shape->slot(), &isFixedSlot, &offset);
@ -5810,6 +5815,10 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
uint32_t offset;
GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
// Instantiate this property for singleton holders, for use during Ion compilation.
if (IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, holder, NameToId(name));
ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native
: ICStub::GetProp_NativePrototype;
@ -5919,6 +5928,10 @@ TryAttachStringGetPropStub(JSContext *cx, HandleScript script, ICGetProp_Fallbac
if (!stringProto)
return false;
// Instantiate this property, for use during Ion compilation.
if (IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, stringProto, NameToId(name));
// For now, only look for properties directly set on String.prototype
RootedId propId(cx, NameToId(name));
RootedShape shape(cx, stringProto->nativeLookup(cx, propId));
@ -7465,6 +7478,11 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
return true;
}
// Keep track of the function's |prototype| property in type
// information, for use during Ion compilation.
if (IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, fun, NameToId(cx->names().prototype));
IonSpew(IonSpew_BaselineIC,
" Generating Call_Scripted stub (fun=%p, %s:%d, cons=%s)",
fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno,

View File

@ -2151,7 +2151,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
ExecutionMode executionMode = gen->info().executionMode();
if (apply->hasSingleTarget()) {
JSFunction *target = apply->getSingleTarget();
if (!CanIonCompile(target, executionMode)) {
if (target->isNative()) {
if (!emitCallInvokeFunction(apply, copyreg))
return false;
emitPopArguments(apply, copyreg);

View File

@ -73,12 +73,6 @@ CanIonCompile(JSScript *script, ExecutionMode cmode)
return false;
}
static inline bool
CanIonCompile(JSFunction *fun, ExecutionMode cmode)
{
return fun->isInterpreted() && CanIonCompile(fun->nonLazyScript(), cmode);
}
static inline bool
CompilingOffThread(JSScript *script, ExecutionMode cmode)
{

View File

@ -271,6 +271,13 @@ IonBuilder::canInlineTarget(JSFunction *target, bool constructing)
return false;
}
// Allow constructing lazy scripts when performing the definite properties
// analysis, as baseline has not been used to warm the caller up yet.
if (target->isInterpretedLazy() && info().executionMode() == DefinitePropertiesAnalysis) {
if (!target->getOrCreateScript(cx))
return false;
}
if (!target->hasScript()) {
IonSpew(IonSpew_Inlining, "Cannot inline due to lack of Non-Lazy script");
return false;
@ -5447,7 +5454,7 @@ IonBuilder::jsop_initelem_array()
needStub = true;
} else if (!initializer->unknownProperties()) {
types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
elemTypes.freeze(constraints());
needStub = true;
}
@ -5994,7 +6001,7 @@ IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton,
if (objType->unknownProperties())
return true;
types::HeapTypeSetKey property = objType->property(idRoot);
types::HeapTypeSetKey property = objType->property(NameToId(name), context());
if (obj != holder) {
if (property.notEmpty(constraints()))
return true;
@ -6076,7 +6083,7 @@ IonBuilder::testSingletonPropertyTypes(MDefinition *obj, JSObject *singleton,
if (object->unknownProperties())
return true;
types::HeapTypeSetKey property = object->property(NameToId(name));
types::HeapTypeSetKey property = object->property(NameToId(name), context());
if (property.notEmpty(constraints()))
return true;
@ -6213,7 +6220,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
Maybe<types::HeapTypeSetKey> propertyTypes;
if (!staticType->unknownProperties()) {
propertyTypes.construct(staticType->property(id));
propertyTypes.construct(staticType->property(id, context()));
if (propertyTypes.ref().configured(constraints(), staticType)) {
// The property has been reconfigured as non-configurable, non-enumerable
// or non-writable.
@ -6223,7 +6230,7 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
}
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), staticType,
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), staticType,
name, baseTypes, /* updateObserved = */ true);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
@ -6266,6 +6273,9 @@ IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psuc
bool
jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes)
{
if (!types)
return inputTypes && inputTypes->empty();
switch (input) {
case MIRType_Undefined:
case MIRType_Null:
@ -6324,7 +6334,7 @@ IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
return jsop_setprop(name);
}
if (!TypeSetIncludes(propertyTypes.actualTypes, value->type(), value->resultTypeSet()))
if (!TypeSetIncludes(propertyTypes.maybeTypes(), value->type(), value->resultTypeSet()))
return jsop_setprop(name);
current->pop();
@ -6733,7 +6743,7 @@ IonBuilder::getElemTryCache(bool *emitted, MDefinition *obj, MDefinition *index)
// Emit GetElementCache.
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
// Always add a barrier if the index might be a string, so that the cache
@ -6782,7 +6792,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
return false;
}
bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(), obj, nullptr, baseTypes);
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(), obj, nullptr, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
@ -7576,7 +7586,8 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name,
jsid id = NameToId(name);
*property = type->property(id);
return property->actualTypes->definiteProperty() &&
return property->maybeTypes() &&
property->maybeTypes()->definiteProperty() &&
!property->configured(constraints(), type);
}
@ -7615,7 +7626,7 @@ TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &
{
cont = true;
types::HeapTypeSetKey propSet = typeObj->property(NameToId(name));
if (!propSet.actualTypes->empty())
if (propSet.maybeTypes() && !propSet.maybeTypes()->empty())
cont = false;
// Note: Callers must explicitly freeze the property type set later on if optimizing.
return true;
@ -8032,7 +8043,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
return emitted;
types::StackTypeSet *baseTypes = types::TypeScript::BytecodeTypes(script(), pc);
bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
current->peek(-1), name, baseTypes);
types::TemporaryTypeSet *types = cloneTypeSet(baseTypes);
@ -8042,13 +8053,14 @@ IonBuilder::jsop_getprop(PropertyName *name)
// Except when loading constants above, always use a call if we are doing
// the definite properties analysis and not actually emitting code, to
// simplify later analysis.
if (info().executionMode() == DefinitePropertiesAnalysis) {
// simplify later analysis. Also skip deeper analysis if there are no known
// types for this operation, as it will always invalidate when executing.
if (info().executionMode() == DefinitePropertiesAnalysis || baseTypes->empty()) {
MDefinition *obj = current->pop();
MCallGetProperty *call = MCallGetProperty::New(obj, name);
current->add(call);
current->push(call);
return resumeAfter(call);
return resumeAfter(call) && pushTypeBarrier(call, types, true);
}
// Try to emit loads from known binary data blocks
@ -8268,7 +8280,7 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name,
useObj = guard;
}
MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.actualTypes->definiteSlot());
MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, property.maybeTypes()->definiteSlot());
if (!barrier)
fixed->setResultType(MIRTypeFromValueType(types->getKnownTypeTag()));
@ -8748,7 +8760,7 @@ IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
if (!getDefiniteSlot(obj->resultTypeSet(), name, &property))
return true;
MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.actualTypes->definiteSlot(), value);
MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, property.maybeTypes()->definiteSlot(), value);
current->add(fixed);
current->push(value);

View File

@ -690,6 +690,16 @@ class IonBuilder : public MIRGenerator
return callerBuilder_ != nullptr;
}
JSContext *context() {
// JSContexts are only available to IonBuilder when running on the main
// thread, which after bug 785905 will only occur when doing eager
// analyses with no available baseline information. Until this bug is
// completed, both the |cx| member and |context()| may be used.
if (info().executionMode() == DefinitePropertiesAnalysis)
return cx;
return NULL;
}
private:
bool init();

View File

@ -221,7 +221,7 @@ IonBuilder::inlineArray(CallInfo &callInfo)
for (uint32_t i = 0; i < initLength; i++) {
MDefinition *value = callInfo.getArg(i);
if (!TypeSetIncludes(elemTypes.actualTypes, value->type(), value->resultTypeSet())) {
if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
elemTypes.freeze(constraints());
return InliningStatus_NotInlined;
}
@ -332,7 +332,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
bool barrier = PropertyReadNeedsTypeBarrier(cx, constraints(),
bool barrier = PropertyReadNeedsTypeBarrier(cx, context(), constraints(),
callInfo.thisArg(), nullptr, returnTypes);
if (barrier)
returnType = MIRType_Value;

View File

@ -2780,12 +2780,12 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
// which are accounted for by type information, i.e. native data properties
// and elements.
if (object->unknownProperties())
if (object->unknownProperties() || observed->empty())
return true;
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
if (!TypeSetIncludes(observed, MIRType_Value, property.actualTypes))
if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes()))
return true;
// Type information for singleton objects is not required to reflect the
@ -2807,7 +2807,8 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
}
bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::StackTypeSet *observed, bool updateObserved)
{
@ -2820,11 +2821,18 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *
if (!obj->isNative())
break;
Value v;
if (HasDataProperty(cx, obj, NameToId(name), &v)) {
if (v.isUndefined())
types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj);
if (!typeObj->unknownProperties()) {
types::HeapTypeSetKey property = typeObj->property(NameToId(name), propertycx);
if (property.maybeTypes()) {
types::TypeSet::TypeList types;
if (!property.maybeTypes()->enumerateTypes(&types))
return false;
if (types.length()) {
observed->addType(cx, types[0]);
break;
observed->addType(cx, types::GetValueType(v));
}
}
}
obj = obj->getProto();
@ -2835,7 +2843,8 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *
}
bool
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
jit::PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::StackTypeSet *observed)
{
@ -2850,7 +2859,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
if (object) {
if (PropertyReadNeedsTypeBarrier(cx, constraints, object, name,
if (PropertyReadNeedsTypeBarrier(cx, propertycx, constraints, object, name,
observed, updateObserved))
{
return true;
@ -2983,7 +2992,10 @@ TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints,
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
if (!property.maybeTypes())
return false;
if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
return false;
// This freeze is not required for correctness, but ensures that we
@ -2994,8 +3006,8 @@ TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints,
if (aggregateProperty.empty()) {
aggregateProperty.construct(property);
} else {
if (!aggregateProperty.ref().actualTypes->isSubset(property.actualTypes) ||
!property.actualTypes->isSubset(aggregateProperty.ref().actualTypes))
if (!aggregateProperty.ref().maybeTypes()->isSubset(property.maybeTypes()) ||
!property.maybeTypes()->isSubset(aggregateProperty.ref().maybeTypes()))
{
return false;
}
@ -3030,7 +3042,7 @@ TryAddTypeBarrierForWrite(types::CompilerConstraintList *constraints,
return false;
types::TemporaryTypeSet *types =
aggregateProperty.ref().actualTypes->clone(GetIonContext()->temp->lifoAlloc());
aggregateProperty.ref().maybeTypes()->clone(GetIonContext()->temp->lifoAlloc());
if (!types)
return false;
@ -3085,7 +3097,7 @@ jit::PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
if (!TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
// Either pobj or pvalue needs to be modified to filter out the
// types which the value could have but are not in the property,
// or a VM call is required. A VM call is always required if pobj
@ -3117,10 +3129,10 @@ jit::PropertyWriteNeedsTypeBarrier(types::CompilerConstraintList *constraints,
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
if (TypeSetIncludes(property.actualTypes, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
continue;
if (!property.actualTypes->empty() || excluded)
if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
return true;
excluded = object->isTypeObject() ? object->asTypeObject() : object->asSingleObject()->getType(GetIonContext()->cx);
}

View File

@ -8993,10 +8993,12 @@ bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefiniti
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
MDefinition *obj);
MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj);
bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
types::TypeObjectKey *object, PropertyName *name,
types::StackTypeSet *observed, bool updateObserved);
bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,
bool PropertyReadNeedsTypeBarrier(JSContext *cx, JSContext *propertycx,
types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name,
types::StackTypeSet *observed);
bool PropertyReadOnPrototypeNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *constraints,

View File

@ -336,6 +336,39 @@ TypeSet::isSubset(TypeSet *other)
return true;
}
bool
TypeSet::enumerateTypes(TypeList *list)
{
/* If any type is possible, there's no need to worry about specifics. */
if (flags & TYPE_FLAG_UNKNOWN)
return list->append(Type::UnknownType());
/* Enqueue type set members stored as bits. */
for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
if (flags & flag) {
Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
if (!list->append(type))
return false;
}
}
/* If any object is possible, skip specifics. */
if (flags & TYPE_FLAG_ANYOBJECT)
return list->append(Type::AnyObjectType());
/* Enqueue specific object types. */
unsigned count = getObjectCount();
for (unsigned i = 0; i < count; i++) {
TypeObjectKey *object = getObject(i);
if (object) {
if (!list->append(Type::ObjectType(object)))
return false;
}
}
return true;
}
inline void
TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
{
@ -343,38 +376,9 @@ TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
* Build all types in the set into a vector before triggering the
* constraint, as doing so may modify this type set.
*/
Vector<Type> types(cx);
/* If any type is possible, there's no need to worry about specifics. */
if (flags & TYPE_FLAG_UNKNOWN) {
if (!types.append(Type::UnknownType()))
TypeList types;
if (!enumerateTypes(&types))
cx->compartment()->types.setPendingNukeTypes(cx);
} else {
/* Enqueue type set members stored as bits. */
for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
if (flags & flag) {
Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
if (!types.append(type))
cx->compartment()->types.setPendingNukeTypes(cx);
}
}
/* If any object is possible, skip specifics. */
if (flags & TYPE_FLAG_ANYOBJECT) {
if (!types.append(Type::AnyObjectType()))
cx->compartment()->types.setPendingNukeTypes(cx);
} else {
/* Enqueue specific object types. */
unsigned count = getObjectCount();
for (unsigned i = 0; i < count; i++) {
TypeObjectKey *object = getObject(i);
if (object) {
if (!types.append(Type::ObjectType(object)))
cx->compartment()->types.setPendingNukeTypes(cx);
}
}
}
}
for (unsigned i = 0; i < types.length(); i++)
constraint->newType(cx, this, types[i]);
@ -566,10 +570,8 @@ class types::CompilerConstraint
CompilerConstraint(const HeapTypeSetKey &property)
: property(property),
expected(property.actualTypes->clone(IonAlloc()))
{
// Note: CompilerConstraintList::add watches for OOM under clone().
}
expected(property.maybeTypes() ? property.maybeTypes()->clone(IonAlloc()) : NULL)
{}
// Generate the type constraint recording the assumption made by this
// compilation. Returns true if the assumption originally made still holds.
@ -580,7 +582,7 @@ void
CompilerConstraintList::add(CompilerConstraint *constraint)
{
#ifdef JS_ION
if (!constraint || !constraint->expected || !constraints.append(constraint))
if (!constraint || !constraints.append(constraint))
setFailed();
#else
MOZ_CRASH();
@ -641,13 +643,16 @@ template <typename T>
bool
CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
{
if (property.actualObject->unknownProperties())
if (property.object()->unknownProperties())
return false;
if (!property.instantiate(cx))
return false;
if (!data.constraintHolds(cx, property, expected))
return false;
property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
property.maybeTypes()->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
/* callExisting = */ false);
return true;
}
@ -683,37 +688,62 @@ TypeObjectKey::newScript()
return nullptr;
}
TypeObject *
TypeObjectKey::maybeType()
{
if (isTypeObject())
return asTypeObject();
if (asSingleObject()->hasLazyType())
return NULL;
return asSingleObject()->type();
}
bool
TypeObjectKey::unknownProperties()
{
#ifdef JS_ION
JSContext *cx = jit::GetIonContext()->cx;
TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
if (!type)
MOZ_CRASH();
if (TypeObject *type = maybeType())
return type->unknownProperties();
#else
MOZ_CRASH();
#endif
return false;
}
HeapTypeSetKey
TypeObjectKey::property(jsid id)
TypeObjectKey::property(jsid id, JSContext *maybecx /* = NULL */)
{
#ifdef JS_ION
JSContext *cx = jit::GetIonContext()->cx;
TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
if (!type)
MOZ_CRASH();
JS_ASSERT(!unknownProperties());
HeapTypeSetKey property;
property.actualObject = type;
property.actualTypes = type->getProperty(cx, id);
if (!property.actualTypes)
MOZ_CRASH();
property.object_ = this;
property.id_ = id;
if (TypeObject *type = maybeType())
property.maybeTypes_ = type->maybeGetProperty(id);
#ifdef JS_ION
// If we are accessing a lazily defined property which actually exists in
// the VM and has not been instantiated yet, instantiate it now if we are
// on the main thread and able to do so.
if (maybecx && !property.maybeTypes() && !JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
JS_ASSERT(CurrentThreadCanAccessRuntime(maybecx->runtime()));
JSObject *singleton = isSingleObject() ? asSingleObject() : asTypeObject()->singleton;
if (singleton && singleton->isNative() && singleton->nativeLookupPure(id)) {
EnsureTrackPropertyTypes(maybecx, singleton, id);
if (TypeObject *type = maybeType())
property.maybeTypes_ = type->maybeGetProperty(id);
}
}
#endif // JS_ION
return property;
#else
MOZ_CRASH();
#endif
}
bool
HeapTypeSetKey::instantiate(JSContext *cx)
{
if (maybeTypes())
return true;
if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx))
return false;
maybeTypes_ = object()->maybeType()->getProperty(cx, id());
return maybeTypes_ != NULL;
}
bool
@ -738,13 +768,18 @@ types::FinishCompilation(JSContext *cx, JSScript *script, ExecutionMode executio
*precompileInfo = RecompileInfo(index);
bool succeeded = true;
for (size_t i = 0; i < constraints->length(); i++) {
CompilerConstraint *constraint = constraints->get(i);
if (!constraint->generateTypeConstraint(cx, *precompileInfo)) {
if (!constraint->generateTypeConstraint(cx, *precompileInfo))
succeeded = false;
}
if (!succeeded) {
types.constrainedOutputs->back().invalidate();
return false;
}
}
return true;
}
@ -766,7 +801,9 @@ class ConstraintDataFreeze
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return property.actualTypes->isSubset(expected);
return expected
? property.maybeTypes()->isSubset(expected)
: property.maybeTypes()->empty();
}
};
@ -842,13 +879,15 @@ TemporaryTypeSet::mightBeType(JSValueType type)
JSValueType
HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
{
if (actualTypes->unknown())
TypeSet *types = maybeTypes();
if (!types || types->unknown())
return JSVAL_TYPE_UNKNOWN;
TypeFlags flags = actualTypes->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
JSValueType type;
if (actualTypes->unknownObject() || actualTypes->getObjectCount())
if (types->unknownObject() || types->getObjectCount())
type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
else
type = GetValueTypeFromTypeFlags(flags);
@ -863,7 +902,7 @@ HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
* that the exact tag is unknown, as it will stay unknown as more types are
* added to the set.
*/
JS_ASSERT_IF(actualTypes->empty(), type == JSVAL_TYPE_UNKNOWN);
JS_ASSERT_IF(types->empty(), type == JSVAL_TYPE_UNKNOWN);
return type;
}
@ -871,7 +910,7 @@ HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
bool
HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints)
{
if (!actualTypes->empty())
if (maybeTypes() && !maybeTypes()->empty())
return true;
freeze(constraints);
return false;
@ -880,7 +919,11 @@ HeapTypeSetKey::notEmpty(CompilerConstraintList *constraints)
bool
HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other)
{
if (!actualTypes->isSubset(other.actualTypes))
if (!maybeTypes() || maybeTypes()->empty()) {
freeze(constraints);
return true;
}
if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
return false;
freeze(constraints);
return true;
@ -898,10 +941,12 @@ TemporaryTypeSet::getSingleton()
JSObject *
HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
{
if (actualTypes->baseFlags() != 0 || actualTypes->getObjectCount() != 1)
TypeSet *types = maybeTypes();
if (!types || types->baseFlags() != 0 || types->getObjectCount() != 1)
return nullptr;
JSObject *obj = actualTypes->getSingleObject(0);
JSObject *obj = types->getSingleObject(0);
if (obj)
freeze(constraints);
@ -912,9 +957,12 @@ HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
bool
HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints)
{
bool result = actualTypes->unknownObject()
|| actualTypes->getObjectCount() > 0
|| actualTypes->hasAnyFlag(TYPE_FLAG_STRING);
TypeSet *types = maybeTypes();
if (!types)
return false;
bool result = types->unknownObject()
|| types->getObjectCount() > 0
|| types->hasAnyFlag(TYPE_FLAG_STRING);
if (!result)
freeze(constraints);
return result;
@ -946,7 +994,7 @@ class ConstraintDataFreezeObjectFlags
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return !invalidateOnNewObjectState(property.actualObject);
return !invalidateOnNewObjectState(property.object()->maybeType());
}
};
@ -955,22 +1003,16 @@ class ConstraintDataFreezeObjectFlags
bool
TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
{
#ifdef JS_ION
JS_ASSERT(flags);
JSContext *cx = jit::GetIonContext()->cx;
TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
if (!type)
MOZ_CRASH();
if (TypeObject *type = maybeType()) {
if (type->hasAnyFlags(flags))
return true;
}
HeapTypeSetKey objectProperty = property(JSID_EMPTY);
constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(flags)));
return false;
#else
MOZ_CRASH();
#endif
}
bool
@ -1047,7 +1089,7 @@ class ConstraintDataFreezeObjectForNewScriptTemplate
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return !invalidateOnNewObjectState(property.actualObject);
return !invalidateOnNewObjectState(property.object()->maybeType());
}
};
@ -1073,7 +1115,7 @@ class ConstraintDataFreezeObjectForTypedArrayBuffer
bool constraintHolds(JSContext *cx,
const HeapTypeSetKey &property, TemporaryTypeSet *expected)
{
return !invalidateOnNewObjectState(property.actualObject);
return !invalidateOnNewObjectState(property.object()->maybeType());
}
};
@ -1170,7 +1212,7 @@ class ConstraintDataFreezeConfiguredProperty
}
}
return !property.actualTypes->configuredProperty();
return !property.maybeTypes()->configuredProperty();
}
};
@ -1179,7 +1221,7 @@ class ConstraintDataFreezeConfiguredProperty
bool
HeapTypeSetKey::configured(CompilerConstraintList *constraints, TypeObjectKey *type)
{
if (actualTypes->configuredProperty())
if (maybeTypes() && maybeTypes()->configuredProperty())
return true;
constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeConfiguredProperty> >(*this, ConstraintDataFreezeConfiguredProperty(type)));
@ -1255,7 +1297,8 @@ TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
// double in their element types (as the conversion may render the type
// information incorrect), nor for non-array objects (as their elements
// may point to emptyObjectElements, which cannot be converted).
if (!property.actualTypes->hasType(Type::DoubleType()) ||
if (!property.maybeTypes() ||
!property.maybeTypes()->hasType(Type::DoubleType()) ||
type->clasp() != &ArrayObject::class_)
{
dontConvert = true;
@ -1763,15 +1806,9 @@ bool
types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints,
HandleScript script)
{
#ifdef JS_ION
JSObject *proto = script->global().getOrCreateArrayPrototype(jit::GetIonContext()->cx);
if (!proto)
return true;
if (JSObject *proto = script->global().maybeGetArrayPrototype())
return PrototypeHasIndexedProperty(constraints, proto);
#else
MOZ_CRASH();
#endif
return true;
}
bool

View File

@ -177,8 +177,7 @@ namespace types {
class TypeCompartment;
class TypeSet;
struct TypeObjectKey;
class TypeObjectKey;
/*
* Information about a single concrete type. We pack this into a single word,
@ -543,6 +542,10 @@ class TypeSet
/* Mark this type set as representing a configured property. */
inline void setConfiguredProperty(ExclusiveContext *cx);
/* Get a list of all types in this set. */
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
bool enumerateTypes(TypeList *list);
/*
* Iterate through the objects in this set. getObjectCount overapproximates
* in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
@ -1219,8 +1222,9 @@ typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,Sy
class HeapTypeSetKey;
/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
struct TypeObjectKey {
// Type set entry for either a JSObject with singleton type or a non-singleton TypeObject.
struct TypeObjectKey
{
static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; }
static TypeObjectKey *getKey(TypeObjectKey *obj) { return obj; }
@ -1259,14 +1263,40 @@ struct TypeObjectKey {
void watchStateChangeForInlinedCall(CompilerConstraintList *constraints);
void watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints);
void watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints);
HeapTypeSetKey property(jsid id);
HeapTypeSetKey property(jsid id, JSContext *maybecx = NULL);
TypeObject *maybeType();
};
// Representation of a heap type property which may or may not be instantiated.
// Heap properties for singleton types are instantiated lazily as they are used
// by the compiler, but this is only done on the main thread. If we are
// compiling off thread and use a property which has not yet been instantiated,
// it will be treated as empty and non-configured and will be instantiated when
// rejoining to the main thread. If it is in fact not empty, the compilation
// will fail; to avoid this, we try to instantiate singleton property types
// during generation of baseline caches.
class HeapTypeSetKey
{
friend class TypeObjectKey;
// Object and property being accessed.
TypeObjectKey *object_;
jsid id_;
// If instantiated, the underlying heap type set.
HeapTypeSet *maybeTypes_;
public:
TypeObject *actualObject;
HeapTypeSet *actualTypes;
HeapTypeSetKey()
: object_(NULL), id_(JSID_EMPTY), maybeTypes_(NULL)
{}
TypeObjectKey *object() const { return object_; }
jsid id() const { return id_; }
HeapTypeSet *maybeTypes() const { return maybeTypes_; }
bool instantiate(JSContext *cx);
void freeze(CompilerConstraintList *constraints);
JSValueType knownTypeTag(CompilerConstraintList *constraints);

View File

@ -422,15 +422,18 @@ TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id)
inline void
EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
{
JS_ASSERT(!obj->hasLazyType());
if (!cx->typeInferenceEnabled() || obj->type()->unknownProperties())
if (!cx->typeInferenceEnabled())
return;
id = IdToTypeId(id);
if (obj->hasSingletonType()) {
AutoEnterAnalysis enter(cx);
if (obj->hasLazyType() && !obj->getType(cx)) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
if (!obj->type()->unknownProperties())
obj->type()->getProperty(cx, id);
}

View File

@ -287,6 +287,12 @@ class GlobalObject : public JSObject
return &self->getPrototype(JSProto_Array).toObject();
}
JSObject *maybeGetArrayPrototype() {
if (arrayClassInitialized())
return &getPrototype(JSProto_Array).toObject();
return NULL;
}
JSObject *getOrCreateBooleanPrototype(JSContext *cx) {
if (booleanClassInitialized())
return &getPrototype(JSProto_Boolean).toObject();