author | Brian Hackett <bhackett1024@gmail.com> |
Thu, 10 Oct 2013 07:45:20 -0600 | |
changeset 150363 | f868d4f8f27e489485aee5063c7e165328883c86 |
parent 150362 | 266e7e5e3a2b484ae6de3d1b73fa2d0f3935f210 |
child 150364 | 44eee084be1d44dc09992cc8d224437f4b8e7c9c |
push id | 25437 |
push user | kwierso@gmail.com |
push date | Fri, 11 Oct 2013 02:00:22 +0000 |
treeherder | mozilla-central@672cd63528d3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 923693 |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jit/IonBuilder.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsinfer.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsinfer.h | file | annotate | diff | comparison | revisions |
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3955,17 +3955,17 @@ IonBuilder::makeInliningDecision(JSFunct IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.", targetScript->filename(), targetScript->lineno); return false; } } // TI calls ObjectStateChange to trigger invalidation of the caller. types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); - targetType->watchStateChange(constraints()); + targetType->watchStateChangeForInlinedCall(constraints()); return true; } uint32_t IonBuilder::selectInliningTargets(ObjectVector &targets, CallInfo &callInfo, BoolVector &choiceSet) { uint32_t totalSize = 0; @@ -4638,17 +4638,17 @@ IonBuilder::createThisScriptedSingleton( RootedObject targetRoot(cx, target); JSObject *templateObject = CreateThisForFunctionWithProto(cx, targetRoot, proto, TenuredObject); if (!templateObject) return nullptr; // Trigger recompilation if the templateObject changes. types::TypeObjectKey *templateType = types::TypeObjectKey::get(templateObject); if (templateType->newScript()) - templateType->watchStateChange(constraints()); + templateType->watchStateChangeForNewScriptTemplate(constraints()); MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject); current->add(createThis); return createThis; } MDefinition * @@ -6881,17 +6881,17 @@ IonBuilder::getTypedArrayElements(MDefin { if (obj->isConstant() && obj->toConstant()->value().isObject()) { TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>(); void *data = tarr->viewData(); // The 'data' pointer can change in rare circumstances // (ArrayBufferObject::changeContents). types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr); - tarrType->watchStateChange(constraints()); + tarrType->watchStateChangeForTypedArrayBuffer(constraints()); obj->setFoldedUnchecked(); return MConstantElements::New(data); } return MTypedArrayElements::New(obj); } MDefinition *
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -623,26 +623,32 @@ class TypeCompilerConstraint : public Ty cx->compartment()->types.addPendingRecompile(cx, compilation); } void newPropertyState(JSContext *cx, TypeSet *source) { if (data.invalidateOnNewPropertyState(source)) cx->compartment()->types.addPendingRecompile(cx, compilation); } - void newObjectState(JSContext *cx, TypeObject *object, bool force) { - if (data.invalidateOnNewObjectState(object, force)) + void newObjectState(JSContext *cx, TypeObject *object) { + // Note: Once the object has unknown properties, no more notifications + // will be sent on changes to its state, so always invalidate any + // associated compilations. + if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) cx->compartment()->types.addPendingRecompile(cx, compilation); } }; template <typename T> bool CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) { + if (property.actualObject->unknownProperties()) + return false; + if (!data.constraintHolds(cx, property, expected)) return false; property.actualTypes->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data), /* callExisting = */ false); return true; } @@ -695,16 +701,17 @@ HeapTypeSetKey TypeObjectKey::property(jsid id) { #ifdef JS_ION JSContext *cx = jit::GetIonContext()->cx; TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject(); if (!type) MOZ_CRASH(); HeapTypeSetKey property; + property.actualObject = type; property.actualTypes = type->getProperty(cx, id); if (!property.actualTypes) MOZ_CRASH(); return property; #else MOZ_CRASH(); #endif } @@ -749,17 +756,17 @@ class ConstraintDataFreeze { public: ConstraintDataFreeze() {} const char *kind() { return "freeze"; } bool invalidateOnNewType(Type type) { return true; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; } + bool invalidateOnNewObjectState(TypeObject *object) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { return property.actualTypes->isSubset(expected); } }; @@ -914,43 +921,37 @@ HeapTypeSetKey::needsBarrier(CompilerCon } namespace { // Constraint which triggers recompilation if an object acquires particular flags. class ConstraintDataFreezeObjectFlags { public: - // Object being queried. - TypeObjectKey *object; - // Flags we are watching for on this object. TypeObjectFlags flags; - ConstraintDataFreezeObjectFlags(TypeObjectKey *object, TypeObjectFlags flags) - : object(object), flags(flags) - {} + ConstraintDataFreezeObjectFlags(TypeObjectFlags flags) + : flags(flags) + { + JS_ASSERT(flags); + } const char *kind() { return "freezeObjectFlags"; } bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(TypeObject *object, bool force) { - return flags ? object->hasAnyFlags(flags) : force; + bool invalidateOnNewObjectState(TypeObject *object) { + return object->hasAnyFlags(flags); } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { - // FIXME: There is not yet any way to test if constraints with no - // associated flags (i.e. those invalidated via |force|) still hold. - TypeObject *type = object->isSingleObject() - ? object->asSingleObject()->type() - : object->asTypeObject(); - return !type->hasAnyFlags(flags); + return !invalidateOnNewObjectState(property.actualObject); } }; } /* anonymous namespace */ bool TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) { @@ -960,30 +961,23 @@ TypeObjectKey::hasFlags(CompilerConstrai JSContext *cx = jit::GetIonContext()->cx; TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject(); if (!type) MOZ_CRASH(); if (type->hasAnyFlags(flags)) return true; HeapTypeSetKey objectProperty = property(JSID_EMPTY); - constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, flags))); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(flags))); return false; #else MOZ_CRASH(); #endif } -void -TypeObjectKey::watchStateChange(CompilerConstraintList *constraints) -{ - HeapTypeSetKey objectProperty = property(JSID_EMPTY); - constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, 0))); -} - bool TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) { if (unknownObject()) return true; /* * Treat type sets containing no objects as having all object flags, @@ -997,34 +991,140 @@ TemporaryTypeSet::hasObjectFlags(Compile TypeObjectKey *object = getObject(i); if (object && object->hasFlags(constraints, flags)) return true; } return false; } -static inline void -ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown, bool force) +namespace { + +// Constraint which triggers recompilation on any type change in an inlined +// script. The freeze constraints added to stack type sets will only directly +// invalidate the script containing those stack type sets. To invalidate code +// for scripts into which the base script was inlined, ObjectStateChange is used. +class ConstraintDataFreezeObjectForInlinedCall +{ + public: + ConstraintDataFreezeObjectForInlinedCall() + {} + + const char *kind() { return "freezeObjectForInlinedCall"; } + + bool invalidateOnNewType(Type type) { return false; } + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } + bool invalidateOnNewObjectState(TypeObject *object) { + // We don't keep track of the exact dependencies the caller has on its + // inlined scripts' type sets, so always invalidate the caller. + return true; + } + + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) + { + return true; + } +}; + +// Constraint which triggers recompilation when the allocation kind of the +// template object for a type's new script changes. +class ConstraintDataFreezeObjectForNewScriptTemplate +{ + gc::AllocKind allocKind; + + public: + ConstraintDataFreezeObjectForNewScriptTemplate(gc::AllocKind allocKind) + : allocKind(allocKind) + {} + + const char *kind() { return "freezeObjectForNewScriptTemplate"; } + + bool invalidateOnNewType(Type type) { return false; } + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } + bool invalidateOnNewObjectState(TypeObject *object) { + return !object->hasNewScript() || object->newScript()->allocKind != allocKind; + } + + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) + { + return !invalidateOnNewObjectState(property.actualObject); + } +}; + +// Constraint which triggers recompilation when the underlying data pointer for +// a typed array changes. +class ConstraintDataFreezeObjectForTypedArrayBuffer +{ + void *viewData; + + public: + ConstraintDataFreezeObjectForTypedArrayBuffer(void *viewData) + : viewData(viewData) + {} + + const char *kind() { return "freezeObjectForTypedArrayBuffer"; } + + bool invalidateOnNewType(Type type) { return false; } + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } + bool invalidateOnNewObjectState(TypeObject *object) { + return object->singleton->as<TypedArrayObject>().viewData() != viewData; + } + + bool constraintHolds(JSContext *cx, + const HeapTypeSetKey &property, TemporaryTypeSet *expected) + { + return !invalidateOnNewObjectState(property.actualObject); + } +}; + +} /* anonymous namespace */ + +void +TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints) +{ + HeapTypeSetKey objectProperty = property(JSID_EMPTY); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> >(objectProperty, ConstraintDataFreezeObjectForInlinedCall())); +} + +void +TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints) +{ + gc::AllocKind kind = asTypeObject()->newScript()->allocKind; + HeapTypeSetKey objectProperty = property(JSID_EMPTY); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForNewScriptTemplate> >(objectProperty, ConstraintDataFreezeObjectForNewScriptTemplate(kind))); +} + +void +TypeObjectKey::watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints) +{ + void *viewData = asSingleObject()->as<TypedArrayObject>().viewData(); + HeapTypeSetKey objectProperty = property(JSID_EMPTY); + constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayBuffer> >(objectProperty, ConstraintDataFreezeObjectForTypedArrayBuffer(viewData))); +} + +static void +ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown) { if (object->unknownProperties()) return; /* All constraints listening to state changes are on the empty id. */ TypeSet *types = object->maybeGetProperty(JSID_EMPTY); /* Mark as unknown after getting the types, to avoid assertion. */ if (markingUnknown) object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES; if (types) { if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = types->constraintList; while (constraint) { - constraint->newObjectState(cx, object, force); + constraint->newObjectState(cx, object); constraint = constraint->next; } } else { JS_ASSERT(!types->constraintList); } } } @@ -1043,17 +1143,17 @@ class ConstraintDataFreezeConfiguredProp {} const char *kind() { return "freezeConfiguredProperty"; } bool invalidateOnNewType(Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet *property) { return property->configuredProperty(); } - bool invalidateOnNewObjectState(TypeObject *object, bool force) { return false; } + bool invalidateOnNewObjectState(TypeObject *object) { return false; } bool constraintHolds(JSContext *cx, const HeapTypeSetKey &property, TemporaryTypeSet *expected) { // Everywhere compiled code depends on definite properties associated // with a type object's newScript, we need to make sure there are // constraints in place which will mark those properties as configured // should the definite properties be invalidated. @@ -1323,26 +1423,16 @@ TemporaryTypeSet::propertyNeedsBarrier(C HeapTypeSetKey property = type->property(id); if (property.needsBarrier(constraints)) return true; } return false; } -/* - * Force recompilation of any jitcode for the script, or of any other script - * which this script was inlined into. - */ -static inline void -AddPendingRecompile(JSContext *cx, JSScript *script) -{ - cx->compartment()->types.addPendingRecompile(cx, script); -} - namespace { /* * As for TypeConstraintFreeze, but describes an implicit freeze constraint * added for stack types within a script. Applies to all compilations of the * script, not just a single one. */ class TypeConstraintFreezeStack : public TypeConstraint @@ -1357,17 +1447,17 @@ class TypeConstraintFreezeStack : public const char *kind() { return "freezeStack"; } void newType(JSContext *cx, TypeSet *source, Type type) { /* * Unlike TypeConstraintFreeze, triggering this constraint once does * not disable it on future changes to the type set. */ - AddPendingRecompile(cx, script_); + cx->compartment()->types.addPendingRecompile(cx, script_); } }; } /* anonymous namespace */ ///////////////////////////////////////////////////////////////////// // TypeCompartment ///////////////////////////////////////////////////////////////////// @@ -1846,17 +1936,17 @@ TypeCompartment::addPendingRecompile(JSC if (script->hasParallelIonScript()) addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); #endif // When one script is inlined into another the caller listens to state // changes on the callee's script, so trigger these to force recompilation // of any such callers. if (script->function() && !script->function()->hasLazyType()) - ObjectStateChange(cx, script->function()->type(), false, true); + ObjectStateChange(cx, script->function()->type(), false); } void TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) { JS_ASSERT(this == &cx->compartment()->types); JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); JS_ASSERT(!target->singleton); @@ -2533,17 +2623,17 @@ TypeObject::markStateChange(ExclusiveCon return; AutoEnterAnalysis enter(cxArg); TypeSet *types = maybeGetProperty(JSID_EMPTY); if (types) { if (JSContext *cx = cxArg->maybeJSContext()) { TypeConstraint *constraint = types->constraintList; while (constraint) { - constraint->newObjectState(cx, this, true); + constraint->newObjectState(cx, this); constraint = constraint->next; } } else { JS_ASSERT(!types->constraintList); } } } @@ -2560,33 +2650,33 @@ TypeObject::setFlags(ExclusiveContext *c JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); } this->flags |= flags; InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); - ObjectStateChange(cx, this, false, false); + ObjectStateChange(cx, this, false); } void TypeObject::markUnknown(ExclusiveContext *cx) { AutoEnterAnalysis enter(cx); JS_ASSERT(cx->compartment()->activeAnalysis); JS_ASSERT(!unknownProperties()); if (!(flags & OBJECT_FLAG_ADDENDUM_CLEARED)) clearAddendum(cx); InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); - ObjectStateChange(cx, this, true, true); + ObjectStateChange(cx, this, true); /* * Existing constraints may have already been added to this object, which we need * to do the right thing for. We can't ensure that we will mark all unknown * objects before they have been accessed, as the __proto__ of a known object * could be dynamically set to an unknown object, and we can decide to ignore * properties of an object during analysis (i.e. hashmaps). Adding unknown for * any properties accessed already accounts for possible values read from them.
--- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -319,21 +319,21 @@ public: /* * For constraints attached to an object property's type set, mark the * property as having been configured. */ virtual void newPropertyState(JSContext *cx, TypeSet *source) {} /* - * For constraints attached to the JSID_EMPTY type set on an object, mark a - * change in one of the object's dynamic property flags. If force is set, - * recompilation is always triggered. + * For constraints attached to the JSID_EMPTY type set on an object, + * indicate a change in one of the object's dynamic property flags or other + * state. */ - virtual void newObjectState(JSContext *cx, TypeObject *object, bool force) {} + virtual void newObjectState(JSContext *cx, TypeObject *object) {} }; /* Flags and other state stored in TypeSet::flags */ enum { TYPE_FLAG_UNDEFINED = 0x1, TYPE_FLAG_NULL = 0x2, TYPE_FLAG_BOOLEAN = 0x4, TYPE_FLAG_INT32 = 0x8, @@ -1251,23 +1251,26 @@ struct TypeObjectKey { const Class *clasp(); TaggedProto proto(); JSObject *singleton(); TypeNewScript *newScript(); bool unknownProperties(); bool hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags); - void watchStateChange(CompilerConstraintList *constraints); + void watchStateChangeForInlinedCall(CompilerConstraintList *constraints); + void watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints); + void watchStateChangeForTypedArrayBuffer(CompilerConstraintList *constraints); HeapTypeSetKey property(jsid id); }; class HeapTypeSetKey { public: + TypeObject *actualObject; HeapTypeSet *actualTypes; void freeze(CompilerConstraintList *constraints); JSValueType knownTypeTag(CompilerConstraintList *constraints); bool configured(CompilerConstraintList *constraints, TypeObjectKey *type); bool notEmpty(CompilerConstraintList *constraints); bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other); JSObject *singleton(CompilerConstraintList *constraints);