mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Analysis and compiler improvements for polymorphic call sites, bug 653962.
This commit is contained in:
parent
d14970c9fe
commit
f1b91c8773
@ -971,7 +971,7 @@ class ScriptAnalysis
|
||||
|
||||
types::TypeBarrier *typeBarriers(uint32 offset) {
|
||||
if (getCode(offset).typeBarriers)
|
||||
pruneTypeBarriers(NULL, offset);
|
||||
pruneTypeBarriers(offset);
|
||||
return getCode(offset).typeBarriers;
|
||||
}
|
||||
types::TypeBarrier *typeBarriers(const jsbytecode *pc) {
|
||||
@ -980,7 +980,18 @@ class ScriptAnalysis
|
||||
void addTypeBarrier(JSContext *cx, const jsbytecode *pc,
|
||||
types::TypeSet *target, types::jstype type);
|
||||
|
||||
void pruneTypeBarriers(JSContext *removecx, uint32 offset);
|
||||
/* Remove obsolete type barriers at the given offset. */
|
||||
void pruneTypeBarriers(uint32 offset);
|
||||
|
||||
/*
|
||||
* Remove still-active type barriers at the given offset. If 'all' is set,
|
||||
* then all barriers are removed, otherwise only those deemed excessive
|
||||
* are removed.
|
||||
*/
|
||||
void breakTypeBarriers(JSContext *cx, uint32 offset, bool all);
|
||||
|
||||
/* Break all type barriers used in computing v. */
|
||||
void breakTypeBarriersSSA(JSContext *cx, const SSAValue &v);
|
||||
|
||||
inline void addPushedType(JSContext *cx, uint32 offset, uint32 which, types::jstype type);
|
||||
|
||||
|
@ -4337,74 +4337,61 @@ JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerDynamic(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerVoid(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNDEFINED);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNDEFINED);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerNull(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_NULL);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_NULL);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerBool(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_BOOLEAN);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerInt(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_INT32);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_INT32);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerFloat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_DOUBLE);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_DOUBLE);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TypeHandlerString(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_STRING);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_STRING);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
@ -4413,9 +4400,6 @@ JS_TypeHandlerNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
TypeFunction *fun = Valueify(jsfun);
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
TypeSet *prototypeTypes =
|
||||
fun->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), true);
|
||||
if (!prototypeTypes)
|
||||
@ -4428,14 +4412,9 @@ JS_TypeHandlerThis(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
if (site->thisTypes)
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
else
|
||||
site->returnTypes->addType(cx, site->thisType);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
|
@ -3222,14 +3222,9 @@ array_TypeSort(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -3237,15 +3232,10 @@ array_TypeInsert(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (site->returnTypes) {
|
||||
/* The return type is an integer (array length). */
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_INT32);
|
||||
}
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
/* The return type is an integer (array length). */
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_INT32);
|
||||
|
||||
for (size_t ind = 0; ind < site->argumentCount; ind++) {
|
||||
site->thisTypes->addSetProperty(cx, site->script, site->pc,
|
||||
@ -3258,14 +3248,8 @@ array_TypeRemove(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
site->thisTypes->addGetProperty(cx, site->script, site->pc, site->returnTypes, JSID_VOID);
|
||||
}
|
||||
|
||||
@ -3274,15 +3258,10 @@ array_TypeSplice(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
|
||||
if (site->returnTypes) {
|
||||
/* Treat the returned array the same as the 'this' array. */
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
/* Treat the returned array the same as the 'this' array. */
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
|
||||
/* All arguments beyond the first two are new array elements. */
|
||||
for (size_t ind = 2; ind < site->argumentCount; ind++) {
|
||||
@ -3297,19 +3276,13 @@ array_TypeConcat(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->hasGlobal()) {
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -3317,14 +3290,9 @@ array_TypeSlice(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->forceThisTypes(cx))
|
||||
return;
|
||||
|
||||
if (site->returnTypes) {
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addFilterPrimitives(cx, site->script, site->returnTypes, false);
|
||||
}
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->thisTypes->addFilterPrimitives(cx, site->script, site->returnTypes, false);
|
||||
}
|
||||
|
||||
/* Handler for all higher order array builtins. */
|
||||
@ -3334,9 +3302,6 @@ array_TypeExtra(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite,
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
|
||||
@ -3504,16 +3469,14 @@ array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->hasGlobal()) {
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
TypeObject *object = site->getInitObject(cx, true);
|
||||
if (!object)
|
||||
return;
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
|
||||
if (object->unknownProperties())
|
||||
return;
|
||||
|
@ -499,7 +499,7 @@ TypeSet::addCondensed(JSContext *cx, JSScript *script)
|
||||
class TypeConstraintProp : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
const jsbytecode *pc;
|
||||
jsbytecode *pc;
|
||||
|
||||
/*
|
||||
* If assign is true, the target is used to update a property of the object.
|
||||
@ -511,7 +511,7 @@ public:
|
||||
/* Property being accessed. */
|
||||
jsid id;
|
||||
|
||||
TypeConstraintProp(JSScript *script, const jsbytecode *pc,
|
||||
TypeConstraintProp(JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id, bool assign)
|
||||
: TypeConstraint("prop", script), pc(pc),
|
||||
assign(assign), target(target), id(id)
|
||||
@ -526,14 +526,14 @@ public:
|
||||
};
|
||||
|
||||
void
|
||||
TypeSet::addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id)
|
||||
{
|
||||
add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, false));
|
||||
}
|
||||
|
||||
void
|
||||
TypeSet::addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id)
|
||||
{
|
||||
add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->pool, script, pc, target, id, true));
|
||||
@ -627,6 +627,43 @@ TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
|
||||
add(cx, ArenaNew<TypeConstraintTransformThis>(cx->compartment->pool, script, target));
|
||||
}
|
||||
|
||||
/*
|
||||
* Constraint which adds a particular type to the 'this' types of all
|
||||
* discovered scripted functions.
|
||||
*/
|
||||
class TypeConstraintPropagateThis : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
jstype type;
|
||||
|
||||
TypeConstraintPropagateThis(JSScript *script, jstype type)
|
||||
: TypeConstraint("propagatethis", script), type(type)
|
||||
{}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, jstype type);
|
||||
};
|
||||
|
||||
void
|
||||
TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type)
|
||||
{
|
||||
/*
|
||||
* If this will definitely be popped by a JSOP_NEW, don't add a constraint
|
||||
* to modify the 'this' types of callees. The initial 'this' value will be
|
||||
* outright ignored.
|
||||
*/
|
||||
SSAValue calleev = SSAValue::PushedValue(pc - script->code, 0);
|
||||
SSAUseChain *uses = script->analysis(cx)->useChain(calleev);
|
||||
|
||||
if (uses && !uses->next && uses->popped) {
|
||||
jsbytecode *callpc = script->code + uses->offset;
|
||||
UntrapOpcode untrap(cx, script, callpc);
|
||||
if (JSOp(*callpc) == JSOP_NEW)
|
||||
return;
|
||||
}
|
||||
|
||||
add(cx, ArenaNew<TypeConstraintPropagateThis>(cx->compartment->pool, script, type));
|
||||
}
|
||||
|
||||
/* Subset constraint which filters out primitive types. */
|
||||
class TypeConstraintFilterPrimitive : public TypeConstraint
|
||||
{
|
||||
@ -660,15 +697,8 @@ TypeSet::addFilterPrimitives(JSContext *cx, JSScript *script, TypeSet *target, b
|
||||
script, target, onlyNullVoid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Cheesy limit on the number of objects we will tolerate in an observed type
|
||||
* set before refusing to add new type barriers for objects.
|
||||
* :FIXME: this heuristic sucks, and doesn't handle calls.
|
||||
*/
|
||||
static const uint32 BARRIER_OBJECT_LIMIT = 10;
|
||||
|
||||
void
|
||||
ScriptAnalysis::pruneTypeBarriers(JSContext *removecx, uint32 offset)
|
||||
ScriptAnalysis::pruneTypeBarriers(uint32 offset)
|
||||
{
|
||||
TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
|
||||
while (*pbarrier) {
|
||||
@ -676,10 +706,31 @@ ScriptAnalysis::pruneTypeBarriers(JSContext *removecx, uint32 offset)
|
||||
if (barrier->target->hasType(barrier->type)) {
|
||||
/* Barrier is now obsolete, it can be removed. */
|
||||
*pbarrier = barrier->next;
|
||||
} else if (removecx && TypeIsObject(barrier->type) &&
|
||||
barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
|
||||
} else {
|
||||
pbarrier = &barrier->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cheesy limit on the number of objects we will tolerate in an observed type
|
||||
* set before refusing to add new type barriers for objects.
|
||||
* :FIXME: this heuristic sucks, and doesn't handle calls.
|
||||
*/
|
||||
static const uint32 BARRIER_OBJECT_LIMIT = 10;
|
||||
|
||||
void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32 offset, bool all)
|
||||
{
|
||||
TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
|
||||
while (*pbarrier) {
|
||||
TypeBarrier *barrier = *pbarrier;
|
||||
if (barrier->target->hasType(barrier->type) ) {
|
||||
/* Barrier is now obsolete, it can be removed. */
|
||||
*pbarrier = barrier->next;
|
||||
} else if (all || (TypeIsObject(barrier->type) &&
|
||||
barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT)) {
|
||||
/* Force removal of the barrier. */
|
||||
barrier->target->addType(removecx, barrier->type);
|
||||
barrier->target->addType(cx, barrier->type);
|
||||
*pbarrier = barrier->next;
|
||||
} else {
|
||||
pbarrier = &barrier->next;
|
||||
@ -687,6 +738,18 @@ ScriptAnalysis::pruneTypeBarriers(JSContext *removecx, uint32 offset)
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v)
|
||||
{
|
||||
if (v.kind() != SSAValue::PUSHED)
|
||||
return;
|
||||
|
||||
uint32 offset = v.pushedOffset();
|
||||
if (JSOp(script->code[offset]) == JSOP_GETPROP)
|
||||
breakTypeBarriersSSA(cx, poppedValue(offset, 0));
|
||||
|
||||
breakTypeBarriers(cx, offset, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Subset constraint for property reads and argument passing which can add type
|
||||
* barriers on the read instead of passing types along.
|
||||
@ -694,10 +757,10 @@ ScriptAnalysis::pruneTypeBarriers(JSContext *removecx, uint32 offset)
|
||||
class TypeConstraintSubsetBarrier : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
const jsbytecode *pc;
|
||||
jsbytecode *pc;
|
||||
TypeSet *target;
|
||||
|
||||
TypeConstraintSubsetBarrier(JSScript *script, const jsbytecode *pc, TypeSet *target)
|
||||
TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
|
||||
: TypeConstraint("subsetBarrier", script), pc(pc), target(target)
|
||||
{
|
||||
JS_ASSERT(!target->intermediate());
|
||||
@ -715,7 +778,7 @@ public:
|
||||
};
|
||||
|
||||
void
|
||||
TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, const jsbytecode *pc, TypeSet *target)
|
||||
TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
|
||||
{
|
||||
add(cx, ArenaNew<TypeConstraintSubsetBarrier>(cx->compartment->pool, script, pc, target));
|
||||
}
|
||||
@ -802,7 +865,7 @@ GetPropertyObject(JSContext *cx, JSScript *script, jstype type)
|
||||
* here, whether via x.f, x[f], or global name accesses.
|
||||
*/
|
||||
static inline void
|
||||
PropertyAccess(JSContext *cx, JSScript *script, const jsbytecode *pc, TypeObject *object,
|
||||
PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
|
||||
bool assign, TypeSet *target, jsid id)
|
||||
{
|
||||
JS_ASSERT_IF(!target, assign);
|
||||
@ -867,8 +930,19 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
}
|
||||
|
||||
TypeObject *object = GetPropertyObject(cx, script, type);
|
||||
if (object)
|
||||
if (object) {
|
||||
PropertyAccess(cx, script, pc, object, assign, target, id);
|
||||
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
if (!object->unknownProperties() &&
|
||||
(JSOp(*pc) == JSOP_CALLPROP || JSOp(*pc) == JSOP_CALLELEM)) {
|
||||
JS_ASSERT(!assign);
|
||||
TypeSet *types = object->getProperty(cx, id, false);
|
||||
if (!types)
|
||||
return;
|
||||
types->addPropagateThis(cx, script, pc, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -911,7 +985,7 @@ void
|
||||
TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
JSScript *script = callsite->script;
|
||||
const jsbytecode *pc = callsite->pc;
|
||||
jsbytecode *pc = callsite->pc;
|
||||
|
||||
if (type == TYPE_UNKNOWN) {
|
||||
/* Monitor calls on unknown functions. */
|
||||
@ -1009,34 +1083,55 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
types->addType(cx, TYPE_UNDEFINED);
|
||||
}
|
||||
|
||||
/* Add a binding for the receiver object of the call. */
|
||||
if (callsite->isNew) {
|
||||
/* Mark the callee as having been invoked with 'new'. */
|
||||
callee->typeSetNewCalled(cx);
|
||||
|
||||
/*
|
||||
* If the script does not return a value then the pushed value is the new
|
||||
* object (typical case).
|
||||
*/
|
||||
if (callsite->returnTypes) {
|
||||
callee->thisTypes()->addSubset(cx, script, callsite->returnTypes);
|
||||
callee->returnTypes()->addFilterPrimitives(cx, script,
|
||||
callsite->returnTypes, false);
|
||||
}
|
||||
callee->thisTypes()->addSubset(cx, script, callsite->returnTypes);
|
||||
callee->returnTypes()->addFilterPrimitives(cx, script, callsite->returnTypes, false);
|
||||
} else {
|
||||
if (callsite->thisTypes) {
|
||||
/* Add a binding for the receiver object of the call. */
|
||||
callsite->thisTypes->addSubset(cx, script, callee->thisTypes());
|
||||
} else {
|
||||
JS_ASSERT(callsite->thisType != TYPE_NULL);
|
||||
callee->thisTypes()->addType(cx, callsite->thisType);
|
||||
}
|
||||
|
||||
/* Add a binding for the return value of the call. */
|
||||
if (callsite->returnTypes)
|
||||
callee->returnTypes()->addSubset(cx, script, callsite->returnTypes);
|
||||
/*
|
||||
* Add a binding for the return value of the call. We don't add a
|
||||
* binding for the receiver object, as this is done with PropagateThis
|
||||
* constraints added by the original JSOP_CALL* op. The type sets we
|
||||
* manipulate here have lost any correlations between particular types
|
||||
* in the 'this' and 'callee' sets, which we want to maintain for
|
||||
* polymorphic JSOP_CALLPROP invocations.
|
||||
*/
|
||||
callee->returnTypes()->addSubset(cx, script, callsite->returnTypes);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
/*
|
||||
* Ignore callees that are calling natives or where the callee is unknown;
|
||||
* the latter will be marked as monitored by a TypeConstraintCall.
|
||||
*/
|
||||
if (type == TYPE_UNKNOWN || !TypeIsObject(type))
|
||||
return;
|
||||
|
||||
TypeObject *object = (TypeObject*) type;
|
||||
if (object->unknownProperties() || !object->isFunction)
|
||||
return;
|
||||
TypeFunction *function = object->asFunction();
|
||||
|
||||
if (!function->script)
|
||||
return;
|
||||
|
||||
JSScript *callee = function->script;
|
||||
|
||||
if (!callee->ensureTypeArray(cx))
|
||||
return;
|
||||
|
||||
callee->thisTypes()->addType(cx, this->type);
|
||||
}
|
||||
|
||||
void
|
||||
TypeConstraintArith::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
@ -2696,7 +2791,19 @@ TypeObject::markUnknown(JSContext *cx)
|
||||
void
|
||||
TypeObject::clearNewScript(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(newScript);
|
||||
JS_ASSERT(!newScriptCleared);
|
||||
newScriptCleared = true;
|
||||
|
||||
/*
|
||||
* It is possible for the object to not have a new script yet but to have
|
||||
* one added in the future. When analyzing properties of new scripts we mix
|
||||
* in adding constraints to trigger clearNewScript with changes to the
|
||||
* type sets themselves (from breakTypeBarriers). It is possible that we
|
||||
* could trigger one of these constraints before AnalyzeNewScriptProperties
|
||||
* has finished, in which case we want to make sure that call fails.
|
||||
*/
|
||||
if (!newScript)
|
||||
return;
|
||||
|
||||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
@ -3136,8 +3243,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
/* Handle as a property access. */
|
||||
PropertyAccess(cx, script, pc, script->getGlobalType(), false, seen, id);
|
||||
|
||||
if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME)
|
||||
if (op == JSOP_CALLGLOBAL || op == JSOP_CALLGNAME) {
|
||||
pushed[1].addType(cx, TYPE_UNKNOWN);
|
||||
pushed[0].addPropagateThis(cx, script, pc, TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
if (CheckNextTest(pc))
|
||||
pushed[0].addType(cx, TYPE_UNDEFINED);
|
||||
@ -3172,8 +3281,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
*/
|
||||
TypeSet *seen = script->bytecodeTypes(pc);
|
||||
seen->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLNAME)
|
||||
if (op == JSOP_CALLNAME) {
|
||||
pushed[1].addType(cx, TYPE_UNKNOWN);
|
||||
pushed[0].addPropagateThis(cx, script, pc, TYPE_UNKNOWN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3213,16 +3324,20 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
unsigned index = GET_UINT16(pc);
|
||||
TypeSet *types = script->upvarTypes(index);
|
||||
types->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLFCSLOT)
|
||||
if (op == JSOP_CALLFCSLOT) {
|
||||
pushed[1].addType(cx, TYPE_UNDEFINED);
|
||||
pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_GETUPVAR_DBG:
|
||||
case JSOP_CALLUPVAR_DBG:
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
if (op == JSOP_CALLUPVAR_DBG)
|
||||
if (op == JSOP_CALLUPVAR_DBG) {
|
||||
pushed[1].addType(cx, TYPE_UNDEFINED);
|
||||
pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSOP_GETARG:
|
||||
@ -3244,8 +3359,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
/* Local 'let' variable. Punt on types for these, for now. */
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
|
||||
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL) {
|
||||
pushed[1].addType(cx, TYPE_UNDEFINED);
|
||||
pushed[0].addPropagateThis(cx, script, pc, TYPE_UNDEFINED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3310,7 +3427,14 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
case JSOP_CALLPROP: {
|
||||
jsid id = GetAtomId(cx, script, pc, 0);
|
||||
TypeSet *seen = script->bytecodeTypes(pc);
|
||||
|
||||
/*
|
||||
* For JSOP_CALLPROP, this will inspect the pc and add PropagateThis
|
||||
* constraints. Different types for the receiver may be correlated with
|
||||
* different callee scripts, and we want to retain such correlations.
|
||||
*/
|
||||
poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
|
||||
|
||||
seen->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLPROP)
|
||||
poppedTypes(pc, 0)->addFilterPrimitives(cx, script, &pushed[1], true);
|
||||
@ -3336,7 +3460,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
case JSOP_GETELEM:
|
||||
case JSOP_CALLELEM: {
|
||||
TypeSet *seen = script->bytecodeTypes(pc);
|
||||
|
||||
/* Ditto the JSOP_CALLPROP case for propagating 'this'. */
|
||||
poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
|
||||
|
||||
seen->addSubset(cx, script, &pushed[0]);
|
||||
if (op == JSOP_CALLELEM)
|
||||
poppedTypes(pc, 1)->addFilterPrimitives(cx, script, &pushed[1], true);
|
||||
@ -3864,7 +3991,7 @@ public:
|
||||
{}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, jstype type) {
|
||||
if (object->newScript && !source->getSingleObject())
|
||||
if (!object->newScriptCleared && !source->getSingleObject())
|
||||
object->clearNewScript(cx);
|
||||
}
|
||||
};
|
||||
@ -4073,6 +4200,13 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS
|
||||
if (JSOp(*calleepc) != JSOP_CALLPROP || calleev.pushedIndex() != 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* This code may not have run yet, break any type barriers involved
|
||||
* in performing the call (for the greater good!).
|
||||
*/
|
||||
analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
|
||||
analysis->breakTypeBarriers(cx, calleepc - script->code, true);
|
||||
|
||||
TypeSet *funcallTypes = analysis->pushedTypes(calleepc, 0);
|
||||
TypeSet *scriptTypes = analysis->pushedTypes(calleepc, 1);
|
||||
|
||||
@ -4088,7 +4222,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Add constraints to clear definite properties from the type
|
||||
* Generate constraints to clear definite properties from the type
|
||||
* should the Function.call or callee itself change in the future.
|
||||
*/
|
||||
TypeIntermediateClearDefinite *funcallTrap =
|
||||
@ -4500,7 +4634,7 @@ JSObject::makeNewType(JSContext *cx, JSScript *newScript)
|
||||
|
||||
Vector<TypeNewScript::Initializer> initializerList(cx);
|
||||
AnalyzeNewScriptProperties(cx, type, newScript, &baseobj, &initializerList);
|
||||
if (baseobj && baseobj->slotSpan() > 0) {
|
||||
if (baseobj && baseobj->slotSpan() > 0 && !type->newScriptCleared) {
|
||||
js::gc::FinalizeKind kind = js::gc::GetGCObjectKind(baseobj->slotSpan());
|
||||
|
||||
/* We should not have overflowed the maximum number of fixed slots for an object. */
|
||||
|
@ -357,18 +357,19 @@ class TypeSet
|
||||
/* Add specific kinds of constraints to this set. */
|
||||
inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
|
||||
void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
|
||||
void addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id);
|
||||
void addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
TypeSet *target, jsid id);
|
||||
void addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target);
|
||||
void addCall(JSContext *cx, TypeCallsite *site);
|
||||
void addArith(JSContext *cx, JSScript *script,
|
||||
TypeSet *target, TypeSet *other = NULL);
|
||||
void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
|
||||
void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, jstype type);
|
||||
void addFilterPrimitives(JSContext *cx, JSScript *script,
|
||||
TypeSet *target, bool onlyNullVoid);
|
||||
void addSubsetBarrier(JSContext *cx, JSScript *script, const jsbytecode *pc, TypeSet *target);
|
||||
void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
|
||||
|
||||
void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
|
||||
bool addCondensed(JSContext *cx, JSScript *script);
|
||||
@ -575,6 +576,9 @@ struct TypeObject
|
||||
/* Mark bit for GC. */
|
||||
bool marked;
|
||||
|
||||
/* If set, newScript information should not be installed on this object. */
|
||||
bool newScriptCleared;
|
||||
|
||||
/*
|
||||
* If non-NULL, objects of this type have always been constructed using
|
||||
* 'new' on the specified script, which adds some number of properties to
|
||||
@ -733,7 +737,7 @@ struct TypeFunction : public TypeObject
|
||||
struct TypeCallsite
|
||||
{
|
||||
JSScript *script;
|
||||
const jsbytecode *pc;
|
||||
jsbytecode *pc;
|
||||
|
||||
/* Whether this is a 'NEW' call. */
|
||||
bool isNew;
|
||||
@ -745,18 +749,12 @@ struct TypeCallsite
|
||||
/* Types of the this variable. */
|
||||
TypeSet *thisTypes;
|
||||
|
||||
/* Any definite type for 'this'. */
|
||||
jstype thisType;
|
||||
|
||||
/* Type set receiving the return value of this call. */
|
||||
TypeSet *returnTypes;
|
||||
|
||||
inline TypeCallsite(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
bool isNew, unsigned argumentCount);
|
||||
|
||||
/* Force creation of thisTypes. */
|
||||
inline bool forceThisTypes(JSContext *cx);
|
||||
|
||||
/* Get the new object at this callsite. */
|
||||
inline TypeObject* getInitObject(JSContext *cx, bool isArray);
|
||||
|
||||
|
@ -92,6 +92,8 @@ GetValueType(JSContext *cx, const Value &val)
|
||||
inline jsid
|
||||
MakeTypeId(JSContext *cx, jsid id)
|
||||
{
|
||||
JS_ASSERT(!JSID_IS_EMPTY(id));
|
||||
|
||||
/*
|
||||
* All integers must map to the aggregate property for index types, including
|
||||
* negative integers.
|
||||
@ -1237,26 +1239,15 @@ TypeSet::make(JSContext *cx, const char *name)
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline
|
||||
TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, const jsbytecode *pc,
|
||||
TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
bool isNew, unsigned argumentCount)
|
||||
: script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
|
||||
thisTypes(NULL), thisType(0), returnTypes(NULL)
|
||||
thisTypes(NULL), returnTypes(NULL)
|
||||
{
|
||||
/* Caller must check for failure. */
|
||||
argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
|
||||
}
|
||||
|
||||
inline bool
|
||||
TypeCallsite::forceThisTypes(JSContext *cx)
|
||||
{
|
||||
if (thisTypes)
|
||||
return true;
|
||||
thisTypes = TypeSet::make(cx, "site_this");
|
||||
if (thisTypes)
|
||||
thisTypes->addType(cx, thisType);
|
||||
return thisTypes != NULL;
|
||||
}
|
||||
|
||||
inline TypeObject *
|
||||
TypeCallsite::getInitObject(JSContext *cx, bool isArray)
|
||||
{
|
||||
@ -1281,6 +1272,7 @@ TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
|
||||
{
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
|
||||
JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
|
||||
JS_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id) != NULL);
|
||||
JS_ASSERT(!unknownProperties());
|
||||
|
||||
|
@ -867,9 +867,6 @@ static void math_TypeArith(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite
|
||||
{
|
||||
types::TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
if (site->isNew)
|
||||
site->returnTypes->addType(cx, types::TYPE_UNKNOWN);
|
||||
|
||||
|
@ -3904,12 +3904,10 @@ static void object_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite
|
||||
TypeObject *object = site->getInitObject(cx, false);
|
||||
if (!object)
|
||||
return;
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
site->returnTypes->addType(cx, (jstype) object);
|
||||
} else {
|
||||
/* The value is converted to an object, don't keep track of the return type. */
|
||||
if (site->returnTypes)
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3203,9 +3203,6 @@ static void type_StringSplit(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsit
|
||||
{
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
if (!site->returnTypes)
|
||||
return;
|
||||
|
||||
if (!site->hasGlobal()) {
|
||||
site->returnTypes->addType(cx, TYPE_UNKNOWN);
|
||||
return;
|
||||
|
@ -4210,12 +4210,13 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType,
|
||||
* without using an IC.
|
||||
*/
|
||||
JSOp op = JSOp(*PC);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
types::TypeSet *types = frame.extra(top).types;
|
||||
if (op == JSOP_GETPROP && types && !types->unknown() && types->getObjectCount() == 1 &&
|
||||
!types->getObject(0)->unknownProperties()) {
|
||||
!types->getObject(0)->unknownProperties() && id == types::MakeTypeId(cx, id)) {
|
||||
JS_ASSERT(usePropCache);
|
||||
types::TypeObject *object = types->getObject(0);
|
||||
types::TypeSet *propertyTypes = object->getProperty(cx, ATOM_TO_JSID(atom), false);
|
||||
types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
|
||||
if (!propertyTypes)
|
||||
return false;
|
||||
if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
|
||||
@ -4704,11 +4705,156 @@ mjit::Compiler::testSingletonPropertyTypes(FrameEntry *top, jsid id, bool *testO
|
||||
return testSingletonProperty(proto, id);
|
||||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_callprop_dispatch(JSAtom *atom)
|
||||
{
|
||||
/*
|
||||
* Check for a CALLPROP which is a dynamic dispatch: every value it can
|
||||
* push is a singleton, and the pushed value is determined by the type of
|
||||
* the object being accessed. Return true if the CALLPROP has been fully
|
||||
* processed, false if no code was generated.
|
||||
*/
|
||||
FrameEntry *top = frame.peek(-1);
|
||||
if (top->isNotType(JSVAL_TYPE_OBJECT))
|
||||
return false;
|
||||
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
if (id != types::MakeTypeId(cx, id))
|
||||
return false;
|
||||
|
||||
types::TypeSet *pushedTypes = pushedTypeSet(0);
|
||||
if (pushedTypes->unknown() || pushedTypes->baseFlags() != 0)
|
||||
return false;
|
||||
|
||||
/* Check every pushed value is a singleton. */
|
||||
for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
|
||||
types::TypeObject *object = pushedTypes->getObject(i);
|
||||
if (object && !object->singleton)
|
||||
return false;
|
||||
}
|
||||
|
||||
types::TypeSet *objTypes = analysis->poppedTypes(PC, 0);
|
||||
if (objTypes->unknown() || objTypes->getObjectCount() == 0)
|
||||
return false;
|
||||
|
||||
pushedTypes->addFreeze(cx);
|
||||
|
||||
/* Map each type in the object to the resulting pushed value. */
|
||||
Vector<JSObject *> results(CompilerAllocPolicy(cx, *this));
|
||||
|
||||
/*
|
||||
* For each type of the base object, check it has no 'own' property for the
|
||||
* accessed id and that its prototype does have such a property.
|
||||
*/
|
||||
uint32 last = 0;
|
||||
for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
|
||||
types::TypeObject *object = objTypes->getObject(i);
|
||||
if (!object) {
|
||||
results.append(NULL);
|
||||
continue;
|
||||
}
|
||||
if (object->unknownProperties() || !object->proto)
|
||||
return false;
|
||||
types::TypeSet *ownTypes = object->getProperty(cx, id, false);
|
||||
if (ownTypes->isOwnProperty(cx, false))
|
||||
return false;
|
||||
|
||||
if (!testSingletonProperty(object->proto, id))
|
||||
return false;
|
||||
|
||||
types::TypeSet *protoTypes = object->proto->getType()->getProperty(cx, id, false);
|
||||
JSObject *singleton = protoTypes->getSingleton(cx);
|
||||
if (!singleton)
|
||||
return false;
|
||||
|
||||
results.append(singleton);
|
||||
last = i;
|
||||
}
|
||||
|
||||
if (oomInVector)
|
||||
return false;
|
||||
|
||||
objTypes->addFreeze(cx);
|
||||
|
||||
/* Done filtering, now generate code which dispatches on the type. */
|
||||
|
||||
if (!top->isType(JSVAL_TYPE_OBJECT)) {
|
||||
Jump notObject = frame.testObject(Assembler::NotEqual, top);
|
||||
stubcc.linkExit(notObject, Uses(1));
|
||||
}
|
||||
|
||||
RegisterID reg = frame.tempRegForData(top);
|
||||
frame.pinReg(reg);
|
||||
RegisterID pushreg = frame.allocReg();
|
||||
frame.unpinReg(reg);
|
||||
|
||||
Address typeAddress(reg, offsetof(JSObject, type));
|
||||
|
||||
Vector<Jump> rejoins(CompilerAllocPolicy(cx, *this));
|
||||
MaybeJump lastMiss;
|
||||
|
||||
for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
|
||||
types::TypeObject *object = objTypes->getObject(i);
|
||||
if (!object) {
|
||||
JS_ASSERT(results[i] == NULL);
|
||||
continue;
|
||||
}
|
||||
if (lastMiss.isSet())
|
||||
lastMiss.get().linkTo(masm.label(), &masm);
|
||||
|
||||
/*
|
||||
* Check that the pushed result is actually in the known pushed types
|
||||
* for the bytecode; this bytecode may have type barriers. Redirect to
|
||||
* the stub to update said pushed types.
|
||||
*/
|
||||
if (!pushedTypes->hasType((types::jstype) results[i]->getType())) {
|
||||
JS_ASSERT(hasTypeBarriers(PC));
|
||||
if (i == last) {
|
||||
stubcc.linkExit(masm.jump(), Uses(1));
|
||||
break;
|
||||
} else {
|
||||
lastMiss.setJump(masm.branchPtr(Assembler::NotEqual, typeAddress, ImmPtr(object)));
|
||||
stubcc.linkExit(masm.jump(), Uses(1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == last) {
|
||||
masm.move(ImmPtr(results[i]), pushreg);
|
||||
break;
|
||||
} else {
|
||||
lastMiss.setJump(masm.branchPtr(Assembler::NotEqual, typeAddress, ImmPtr(object)));
|
||||
masm.move(ImmPtr(results[i]), pushreg);
|
||||
rejoins.append(masm.jump());
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < rejoins.length(); i++)
|
||||
rejoins[i].linkTo(masm.label(), &masm);
|
||||
|
||||
stubcc.leave();
|
||||
stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1);
|
||||
OOL_STUBCALL(stubs::CallProp, REJOIN_FALLTHROUGH);
|
||||
|
||||
frame.dup();
|
||||
// THIS THIS
|
||||
|
||||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, pushreg);
|
||||
// THIS THIS FUN
|
||||
|
||||
frame.shift(-2);
|
||||
// FUN THIS
|
||||
|
||||
stubcc.rejoin(Changes(2));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_callprop(JSAtom *atom)
|
||||
{
|
||||
FrameEntry *top = frame.peek(-1);
|
||||
|
||||
/* If the CALLPROP will definitely be fetching a particular value, nop it. */
|
||||
bool testObject;
|
||||
JSObject *singleton = pushedSingleton(0);
|
||||
if (singleton && singleton->isFunction() && !hasTypeBarriers(PC) &&
|
||||
@ -4738,6 +4884,12 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for a dynamic dispatch. */
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
if (jsop_callprop_dispatch(atom))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the incoming type will never PIC, take slow path. */
|
||||
if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) {
|
||||
if (top->getKnownType() == JSVAL_TYPE_STRING)
|
||||
@ -4766,13 +4918,14 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed
|
||||
* Set the property directly if we are accessing a known object which
|
||||
* always has the property in a particular inline slot.
|
||||
*/
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
types::TypeSet *types = frame.extra(lhs).types;
|
||||
if (JSOp(*PC) == JSOP_SETPROP &&
|
||||
if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) &&
|
||||
types && !types->unknown() && types->getObjectCount() == 1 &&
|
||||
!types->getObject(0)->unknownProperties()) {
|
||||
JS_ASSERT(usePropCache);
|
||||
types::TypeObject *object = types->getObject(0);
|
||||
types::TypeSet *propertyTypes = object->getProperty(cx, ATOM_TO_JSID(atom), false);
|
||||
types::TypeSet *propertyTypes = object->getProperty(cx, id, false);
|
||||
if (!propertyTypes)
|
||||
return false;
|
||||
if (propertyTypes->isDefiniteProperty() && !propertyTypes->isOwnProperty(cx, true)) {
|
||||
@ -5679,8 +5832,9 @@ mjit::Compiler::jsop_getgname(uint32 index)
|
||||
}
|
||||
|
||||
/* Get the type of the global. */
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
JSValueType type = JSVAL_TYPE_UNKNOWN;
|
||||
if (cx->typeInferenceEnabled() && globalObj->isGlobal() &&
|
||||
if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
|
||||
!globalObj->getType()->unknownProperties()) {
|
||||
/*
|
||||
* Get the type tag for the global either straight off it (in case this
|
||||
@ -5692,7 +5846,7 @@ mjit::Compiler::jsop_getgname(uint32 index)
|
||||
if (JSOp(*PC) == JSOP_GETGNAME || JSOp(*PC) == JSOP_CALLGNAME)
|
||||
types = pushedTypeSet(0);
|
||||
else
|
||||
types = globalObj->getType()->getProperty(cx, ATOM_TO_JSID(atom), false);
|
||||
types = globalObj->getType()->getProperty(cx, id, false);
|
||||
if (!types)
|
||||
return;
|
||||
type = types->getKnownTypeTag(cx);
|
||||
@ -5895,7 +6049,8 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache, bool popGuara
|
||||
return;
|
||||
}
|
||||
|
||||
if (cx->typeInferenceEnabled() && globalObj->isGlobal() &&
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
if (cx->typeInferenceEnabled() && globalObj->isGlobal() && id == types::MakeTypeId(cx, id) &&
|
||||
!globalObj->getType()->unknownProperties()) {
|
||||
/*
|
||||
* Note: object branding is disabled when inference is enabled. With
|
||||
@ -5903,7 +6058,7 @@ mjit::Compiler::jsop_setgname(JSAtom *atom, bool usePropertyCache, bool popGuara
|
||||
* can't get a function later and cause the global object to become
|
||||
* branded, requiring a shape change if it changes again.
|
||||
*/
|
||||
types::TypeSet *types = globalObj->getType()->getProperty(cx, ATOM_TO_JSID(atom), false);
|
||||
types::TypeSet *types = globalObj->getType()->getProperty(cx, id, false);
|
||||
if (!types)
|
||||
return;
|
||||
const js::Shape *shape = globalObj->nativeLookup(ATOM_TO_JSID(atom));
|
||||
|
@ -646,6 +646,7 @@ class Compiler : public BaseCompiler
|
||||
bool jsop_callprop_obj(JSAtom *atom);
|
||||
bool jsop_callprop_str(JSAtom *atom);
|
||||
bool jsop_callprop_generic(JSAtom *atom);
|
||||
bool jsop_callprop_dispatch(JSAtom *atom);
|
||||
bool jsop_instanceof();
|
||||
void jsop_name(JSAtom *atom, JSValueType type);
|
||||
bool jsop_xname(JSAtom *atom);
|
||||
|
@ -712,7 +712,7 @@ FrameState::syncForAllocation(RegisterAllocation *alloc, bool inlineReturn, Uses
|
||||
|
||||
if (deadEntry(fe, uses.nuses))
|
||||
continue;
|
||||
if (topEntry && fe >= topEntry && !isTemporary(fe)) {
|
||||
if (inlineReturn && fe >= topEntry && !isTemporary(fe)) {
|
||||
/*
|
||||
* The return value has already been stored, so there is no need to
|
||||
* keep any of the entries for this frame or for values popped once
|
||||
@ -727,7 +727,17 @@ FrameState::syncForAllocation(RegisterAllocation *alloc, bool inlineReturn, Uses
|
||||
|
||||
/* Force syncs for locals which are dead at the current PC. */
|
||||
if (isLocal(fe) && !a->analysis->slotEscapes(entrySlot(fe))) {
|
||||
Lifetime *lifetime = variableLive(fe, a->PC);
|
||||
Lifetime *lifetime = a->analysis->liveness(entrySlot(fe)).live(a->PC - a->script->code);
|
||||
if (!lifetime)
|
||||
fakeSync(fe);
|
||||
}
|
||||
|
||||
/* If returning from a script, fake syncs for dead locals in the immediate parent. */
|
||||
if (inlineReturn && fe >= a->parent->locals &&
|
||||
fe - a->parent->locals < a->parent->script->nfixed &&
|
||||
!a->parent->analysis->slotEscapes(frameSlot(a->parent, fe))) {
|
||||
const LifetimeVariable &var = a->parent->analysis->liveness(frameSlot(a->parent, fe));
|
||||
Lifetime *lifetime = var.live(a->parent->PC - a->parent->script->code);
|
||||
if (!lifetime)
|
||||
fakeSync(fe);
|
||||
}
|
||||
|
@ -2752,14 +2752,14 @@ stubs::TypeBarrierHelper(VMFrame &f, uint32 which)
|
||||
result = f.regs.sp[0];
|
||||
|
||||
/*
|
||||
* Prune type barriers at this bytecode if we have added many objects to
|
||||
* Break type barriers at this bytecode if we have added many objects to
|
||||
* the target already. This isn't needed if inference results for the
|
||||
* script have been destroyed, as we will reanalyze and prune type barriers
|
||||
* as they are regenerated.
|
||||
*/
|
||||
if (f.script()->hasAnalysis() && f.script()->analysis(f.cx)->ranInference()) {
|
||||
AutoEnterTypeInference enter(f.cx);
|
||||
f.script()->analysis(f.cx)->pruneTypeBarriers(f.cx, f.pc() - f.script()->code);
|
||||
f.script()->analysis(f.cx)->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false);
|
||||
}
|
||||
|
||||
f.script()->typeMonitor(f.cx, f.pc(), result);
|
||||
|
Loading…
Reference in New Issue
Block a user