[INFER] Fix inference freeze interface to directly reflect type information used by the Compiler.

This commit is contained in:
Brian Hackett 2010-11-03 15:07:49 -07:00
parent 545b668654
commit 631b2784ef
6 changed files with 106 additions and 352 deletions

View File

@ -309,12 +309,6 @@ class Script
/* Array of local variable names, computed by js_GetLocalNameArray. */
jsuword *localNames;
/* Whether this script is considered to be compiled, and types have been frozen. */
bool compiled;
/* Whether this script needs recompilation. */
bool recompileNeeded;
void setFunction(JSContext *cx, JSFunction *fun);
inline bool isEval() { return parent && !function; }
@ -335,14 +329,6 @@ class Script
/* Analyzes a bytecode, generating type constraints describing its behavior. */
void analyzeTypes(JSContext *cx, Bytecode *codeType);
/*
* Add new constraints for a bytecode monitoring changes on type sets which can
* affect what the bytecode does. Performed after analysis has finished and the
* type sets hopefully won't change further.
*/
void freezeTypes(JSContext *cx, Bytecode *codeType);
void freezeAllTypes(JSContext *cx);
/*
* Get the name to use for the local with specified index. Stack indicates the
* point of the access, for looking up let variables.

View File

@ -828,118 +828,50 @@ TypeConstraintMonitorRead::newType(JSContext *cx, TypeSet *source, jstype type)
/////////////////////////////////////////////////////////////////////
/*
* Constraint which logs changes which occur after a script has been frozen,
* at a bytecode granularity.
* Constraint which triggers recompilation of a script if a possible new JSValueType
* tag is realized for a type set.
*/
class TypeConstraintFreeze : public TypeConstraint
class TypeConstraintFreezeTypeTag : public TypeConstraint
{
public:
analyze::Bytecode *code;
JSScript *script;
bool isConstructing;
TypeConstraintFreeze(analyze::Bytecode *_code)
: TypeConstraint("freeze"), code(_code)
/*
* Whether the type tag has been marked unknown due to a type change which
* occurred after this constraint was generated (and which triggered recompilation).
*/
bool typeUnknown;
TypeConstraintFreezeTypeTag(JSScript *script, bool isConstructing)
: TypeConstraint("freezeTypeTag"),
script(script), isConstructing(isConstructing), typeUnknown(false)
{}
void newType(JSContext *cx, TypeSet *source, jstype type)
{
if (typeUnknown)
return;
if (type != TYPE_UNKNOWN && TypeIsObject(type)) {
/*
* Ignore new objects when either (a) this is a non-function object
* and there are already other known objects, or (b) this is a function
* object and there were already at least two objects of any kind
* (could not treat as a direct call). This can underapproximate the
* number of recompilations actually required.
*/
TypeObject *obj = (TypeObject*) type;
if (obj->isFunction) {
if (source->objectCount >= 3)
return;
} else if (source->objectCount >= 2) {
/* Ignore new objects when the type set already has other objects. */
if (source->objectCount >= 2) {
JS_ASSERT(source->typeFlags == TYPE_FLAG_OBJECT);
return;
}
}
cx->compartment->types.recompileScript(code);
typeUnknown = true;
/* Need to trigger recompilation of the script here. :FIXME: bug 608746 */
}
};
void
TypeSet::addFreeze(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code)
TypeSet::addFreezeTypeTag(JSContext *cx, JSScript *script, bool isConstructing)
{
JS_ASSERT(this->pool == &pool);
add(cx, ArenaNew<TypeConstraintFreeze>(pool, code), false);
}
/*
* Constraint which logs changes to the types of a property of any object which
* a type set can refer to.
*/
class TypeConstraintFreezeProp : public TypeConstraint
{
public:
analyze::Bytecode *code;
jsid id;
TypeConstraintFreezeProp(analyze::Bytecode *_code, jsid _id)
: TypeConstraint("freezeprop"), code(_code), id(_id)
{}
void newType(JSContext *cx, TypeSet *source, jstype type)
{
if (type == TYPE_UNKNOWN)
return;
TypeObject *object = GetPropertyObject(cx, type);
if (!object)
return;
TypeSet *types = object->properties(cx).getVariable(cx, id);
types->addFreeze(cx, object->pool(), code);
}
};
void
TypeSet::addFreezeProp(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, jsid id)
{
JS_ASSERT(this->pool == &pool);
add(cx, ArenaNew<TypeConstraintFreezeProp>(pool, code, id), false);
}
/*
* Constraint which logs changes to element accesses on an object, depending on
* the type of the access.
*/
class TypeConstraintFreezeElem : public TypeConstraint
{
public:
analyze::Bytecode *code;
/* Fields about the element access, as for TypeConstraintElem. */
TypeSet *object;
TypeConstraintFreezeElem(analyze::Bytecode *code, TypeSet *object)
: TypeConstraint("freezeelem"), code(code), object(object)
{}
void newType(JSContext *cx, TypeSet *source, jstype type)
{
if (type == TYPE_INT32) {
object->addFreezeProp(cx, code->pool(), code, JSID_VOID);
} else {
/*
* Accesses at this bytecode are monitored already, don't need
* any freeze constraints.
*/
JS_ASSERT(code->monitorNeeded);
}
}
};
void
TypeSet::addFreezeElem(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *object)
{
JS_ASSERT(this->pool == &pool);
add(cx, ArenaNew<TypeConstraintFreezeElem>(pool, code, object), false);
JS_ASSERT(this->pool == &script->analysis->pool);
add(cx, ArenaNew<TypeConstraintFreezeTypeTag>(script->analysis->pool, script, isConstructing), false);
}
/////////////////////////////////////////////////////////////////////
@ -1170,8 +1102,6 @@ TypeCompartment::print(JSContext *cx, JSCompartment *compartment)
}
fprintf(out, " (%u over)\n", typeCountOver);
if (recompilations)
fprintf(out, "Recompilations: %u\n", recompilations);
fprintf(out, "Time: %.2f ms\n", millis);
// for debugging regressions.
@ -1504,15 +1434,6 @@ JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value
fputs("\n", cx->typeOut());
#endif
if (!code.script->compiled)
analysis->freezeAllTypes(cx);
if (code.script->recompileNeeded) {
fprintf(cx->typeOut(), "Recompile: #%u\n", analysis->id);
code.script->recompileNeeded = false;
cx->compartment->types.recompilations++;
}
if (!useCount || code.missingTypes)
return;
@ -2749,191 +2670,6 @@ Script::analyzeTypes(JSContext *cx, Bytecode *code)
}
}
void
Script::freezeAllTypes(JSContext *cx)
{
JS_ASSERT(!compiled);
compiled = true;
unsigned offset = 0;
while (offset < script->length) {
Bytecode *code = codeArray[offset];
if (code && code->analyzed)
freezeTypes(cx, code);
offset += GetBytecodeLength(script->code + offset);
}
}
void
Script::freezeTypes(JSContext *cx, Bytecode *code)
{
unsigned offset = code->offset;
unsigned useCount = GetUseCount(script, offset);
JS_ASSERT(code->analyzed);
jsbytecode *pc = script->code + offset;
JSOp op = (JSOp)*pc;
switch (op) {
case JSOP_IFEQ:
case JSOP_IFEQX:
case JSOP_IFNE:
case JSOP_IFNEX:
case JSOP_LOOKUPSWITCH:
case JSOP_LOOKUPSWITCHX:
case JSOP_TABLESWITCH:
case JSOP_TABLESWITCHX:
case JSOP_LSH:
case JSOP_RSH:
case JSOP_URSH:
case JSOP_BITOR:
case JSOP_BITXOR:
case JSOP_BITAND:
case JSOP_BITNOT:
case JSOP_EQ:
case JSOP_NE:
case JSOP_LT:
case JSOP_LE:
case JSOP_GT:
case JSOP_GE:
case JSOP_NOT:
case JSOP_STRICTEQ:
case JSOP_STRICTNE:
case JSOP_IN:
case JSOP_DIV:
case JSOP_OR:
case JSOP_ORX:
case JSOP_AND:
case JSOP_ANDX:
case JSOP_GETPROP:
case JSOP_GETXPROP:
case JSOP_CALLPROP:
case JSOP_GETELEM:
case JSOP_CALLELEM:
case JSOP_LENGTH:
case JSOP_ADD:
case JSOP_SUB:
case JSOP_MUL:
case JSOP_MOD:
case JSOP_NEG:
for (unsigned i = 0; i < useCount; i++)
code->popped(i)->addFreeze(cx, pool, code);
break;
case JSOP_INCNAME:
case JSOP_DECNAME:
case JSOP_NAMEINC:
case JSOP_NAMEDEC: {
jsid id = GetAtomId(cx, this, pc, 0);
VariableSet *vars = SearchScope(cx, this, code->inStack, id);
if (vars) {
TypeSet *types = vars->getVariable(cx, id);
types->addFreeze(cx, *vars->pool, code);
}
break;
}
case JSOP_INCGNAME:
case JSOP_DECGNAME:
case JSOP_GNAMEINC:
case JSOP_GNAMEDEC: {
jsid id = GetAtomId(cx, this, pc, 0);
TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
types->addFreeze(cx, cx->compartment->types.pool, code);
break;
}
case JSOP_INCGLOBAL:
case JSOP_DECGLOBAL:
case JSOP_GLOBALINC:
case JSOP_GLOBALDEC: {
jsid id = GetGlobalId(cx, this, pc);
TypeSet *types = GetGlobalProperties(cx)->getVariable(cx, id);
types->addFreeze(cx, cx->compartment->types.pool, code);
break;
}
case JSOP_INCARG:
case JSOP_DECARG:
case JSOP_ARGINC:
case JSOP_ARGDEC:
case JSOP_GETARGPROP: {
jsid id = getArgumentId(GET_ARGNO(pc));
TypeSet *types = localTypes.getVariable(cx, id);
types->addFreeze(cx, pool, code);
break;
}
case JSOP_INCLOCAL:
case JSOP_DECLOCAL:
case JSOP_LOCALINC:
case JSOP_LOCALDEC:
case JSOP_GETLOCALPROP: {
jsid id = getLocalId(GET_SLOTNO(pc), code->inStack);
TypeSet *types = evalParent()->localTypes.getVariable(cx, id);
types->addFreeze(cx, evalParent()->pool, code);
break;
}
case JSOP_SETPROP:
case JSOP_SETMETHOD:
code->popped(1)->addFreeze(cx, pool, code);
break;
case JSOP_INCPROP:
case JSOP_DECPROP:
case JSOP_PROPINC:
case JSOP_PROPDEC: {
jsid id = GetAtomId(cx, this, pc, 0);
code->popped(0)->addFreeze(cx, pool, code);
code->popped(0)->addFreezeProp(cx, pool, code, id);
break;
}
case JSOP_GETTHISPROP:
thisTypes.addFreeze(cx, pool, code);
break;
case JSOP_SETELEM:
code->popped(1)->addFreeze(cx, pool, code);
code->popped(2)->addFreeze(cx, pool, code);
break;
case JSOP_INCELEM:
case JSOP_DECELEM:
case JSOP_ELEMINC:
case JSOP_ELEMDEC:
code->popped(0)->addFreeze(cx, pool, code);
code->popped(1)->addFreeze(cx, pool, code);
code->popped(0)->addFreezeElem(cx, pool, code, code->popped(1));
break;
case JSOP_CALL:
case JSOP_EVAL:
case JSOP_APPLY:
case JSOP_NEW: {
/*
* The only value affecting the behavior of a call is the callee,
* everything else is just copied around.
*/
TypeSet *funTypes = code->popped(useCount - 1);
funTypes->addFreeze(cx, pool, code);
break;
}
default:
break;
}
}
/////////////////////////////////////////////////////////////////////
// Printing
/////////////////////////////////////////////////////////////////////

View File

@ -224,13 +224,15 @@ struct TypeSet
void addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid);
void addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target);
/* For simulating recompilation. */
void addFreeze(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code);
void addFreezeProp(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, jsid id);
void addFreezeElem(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *object);
/* Constraints inducing recompilation. */
void addFreezeTypeTag(JSContext *cx, JSScript *script, bool isConstructing);
/* Get any type tag which all values in this set must have. */
inline JSValueType getKnownTypeTag();
/*
* Get any type tag which all values in this set must have. Should this type
* set change in the future so that another type tag is possible, mark script
* for recompilation.
*/
inline JSValueType getKnownTypeTag(JSContext *cx, JSScript *script, bool isConstructing);
/*
* Make an intermediate type set with the specified debugging name,
@ -719,9 +721,6 @@ struct TypeCompartment
*/
uint64_t analysisTime;
/* Number of times a script needed to be recompiled. */
unsigned recompilations;
/* Counts of stack type sets with some number of possible operand types. */
static const unsigned TYPE_COUNT_LIMIT = 4;
unsigned typeCounts[TYPE_COUNT_LIMIT];
@ -766,9 +765,6 @@ struct TypeCompartment
/* Monitor future effects on a bytecode. */
inline void monitorBytecode(analyze::Bytecode *code);
/* Mark a bytecode's script as needing eventual recompilation. */
inline void recompileScript(analyze::Bytecode *code);
};
} /* namespace types */

View File

@ -838,27 +838,13 @@ TypeCompartment::resolvePending(JSContext *cx)
resolving = false;
}
inline void
TypeCompartment::recompileScript(analyze::Bytecode *code)
{
JS_ASSERT(code->script->compiled);
if (!code->script->recompileNeeded) {
#ifdef DEBUG
fprintf(out, "MarkJIT: #%u:%05u\n", code->script->id, code->offset);
#endif
code->script->recompileNeeded = true;
}
}
inline void
TypeCompartment::monitorBytecode(analyze::Bytecode *code)
{
if (code->monitorNeeded)
return;
if (code->script->compiled)
recompileScript(code);
/* :FIXME: bug 608746 trigger recompilation of the script if necessary. */
#ifdef JS_TYPES_DEBUG_SPEW
fprintf(out, "addMonitorNeeded: #%u:%05u\n", code->script->id, code->offset);
@ -1134,24 +1120,36 @@ TypeSet::addType(JSContext *cx, jstype type)
}
inline JSValueType
TypeSet::getKnownTypeTag()
TypeSet::getKnownTypeTag(JSContext *cx, JSScript *script, bool isConstructing)
{
JSValueType type;
switch (typeFlags) {
case TYPE_FLAG_UNDEFINED:
return JSVAL_TYPE_UNDEFINED;
type = JSVAL_TYPE_UNDEFINED;
break;
case TYPE_FLAG_NULL:
return JSVAL_TYPE_NULL;
type = JSVAL_TYPE_NULL;
break;
case TYPE_FLAG_BOOLEAN:
return JSVAL_TYPE_BOOLEAN;
type = JSVAL_TYPE_BOOLEAN;
break;
case TYPE_FLAG_INT32:
return JSVAL_TYPE_INT32;
type = JSVAL_TYPE_INT32;
break;
case TYPE_FLAG_STRING:
return JSVAL_TYPE_STRING;
type = JSVAL_TYPE_STRING;
break;
case TYPE_FLAG_OBJECT:
return JSVAL_TYPE_OBJECT;
type = JSVAL_TYPE_OBJECT;
break;
default:
/* Return directly to avoid adding a type constraint. */
return JSVAL_TYPE_UNKNOWN;
}
addFreezeTypeTag(cx, script, isConstructing);
return type;
}
inline TypeSet *

View File

@ -114,6 +114,10 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
#if defined JS_TRACER
,addTraceHints(cx->traceJitEnabled)
#endif
#if defined JS_TYPE_INFERENCE
,argumentTypes(ContextAllocPolicy(cx))
,localTypes(ContextAllocPolicy(cx))
#endif
{
}
@ -158,16 +162,49 @@ mjit::Compiler::performCompilation(JITScript **jitp)
JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
script->filename, script->lineno, script->length);
uint32 nargs = fun ? fun->nargs : 0;
if (!frame.init(nargs) || !stubcc.init(nargs))
return Compile_Abort;
#ifdef JS_TYPE_INFERENCE
this->analysis = script->analyze(cx);
#else
/* Fill in known types of arguments and locals. */
if (!argumentTypes.reserve(nargs))
return Compile_Error;
for (unsigned i = 0; i < nargs; i++) {
jsid id = analysis->getArgumentId(i);
JSValueType type = JSVAL_TYPE_UNKNOWN;
if (!JSID_IS_VOID(id)) {
types::TypeSet *types = analysis->localTypes.getVariable(cx, id);
type = types->getKnownTypeTag(cx, script, isConstructing);
}
argumentTypes.append(type);
}
if (!localTypes.reserve(script->nfixed))
return Compile_Error;
for (unsigned i = 0; i < script->nfixed; i++) {
jsid id = analysis->getLocalId(i, NULL);
JSValueType type = JSVAL_TYPE_UNKNOWN;
if (!analysis->localHasUseBeforeDef(i) && !JSID_IS_VOID(id)) {
types::TypeSet *types = analysis->localTypes.getVariable(cx, id);
type = types->getKnownTypeTag(cx, script, isConstructing);
}
localTypes.append(type);
}
#else /* JS_TYPE_INFERENCE */
analyze::Script analysis_;
PodZero(&analysis_);
analysis_.init(script);
analysis_.analyze(cx);
this->analysis = &analysis_;
#endif
#endif /* JS_TYPE_INFERENCE */
if (analysis->OOM())
return Compile_Error;
@ -176,10 +213,6 @@ mjit::Compiler::performCompilation(JITScript **jitp)
return Compile_Abort;
}
uint32 nargs = fun ? fun->nargs : 0;
if (!frame.init(nargs) || !stubcc.init(nargs))
return Compile_Abort;
jumpMap = (Label *)cx->malloc(sizeof(Label) * script->length);
if (!jumpMap)
return Compile_Error;
@ -4628,7 +4661,8 @@ mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth)
}
for (uint32 i = 0; i < stackDepth; i++) {
types::TypeStack *stack = analysis->getCode(PC).inStack;
JSValueType type = analysis->getStackTypes(script->nfixed + i, stack)->getKnownTypeTag();
types::TypeSet *types = analysis->getStackTypes(script->nfixed + i, stack);
JSValueType type = types->getKnownTypeTag(cx, script, isConstructing);
if (type != JSVAL_TYPE_UNKNOWN) {
FrameEntry *fe = frame.getLocal(script->nfixed + i);
frame.learnType(fe, type, true);
@ -4641,9 +4675,8 @@ JSValueType
mjit::Compiler::knownArgumentType(uint32 arg)
{
#ifdef JS_TYPE_INFERENCE
jsid id = analysis->getArgumentId(arg);
if (!JSID_IS_VOID(id))
return analysis->localTypes.getVariable(cx, id)->getKnownTypeTag();
JS_ASSERT(fun && arg < fun->nargs);
return argumentTypes[arg];
#endif
return JSVAL_TYPE_UNKNOWN;
}
@ -4652,9 +4685,8 @@ JSValueType
mjit::Compiler::knownLocalType(uint32 local)
{
#ifdef JS_TYPE_INFERENCE
jsid id = analysis->getLocalId(local, NULL);
if (!analysis->localHasUseBeforeDef(local) && !JSID_IS_VOID(id))
return analysis->localTypes.getVariable(cx, id)->getKnownTypeTag();
JS_ASSERT(local < script->nfixed);
return localTypes[local];
#endif
return JSVAL_TYPE_UNKNOWN;
}
@ -4663,7 +4695,8 @@ JSValueType
mjit::Compiler::knownPushedType(uint32 pushed)
{
#ifdef JS_TYPE_INFERENCE
return analysis->getCode(PC).pushed(pushed)->getKnownTypeTag();
types::TypeSet *types = analysis->getCode(PC).pushed(pushed);
return types->getKnownTypeTag(cx, script, isConstructing);
#endif
return JSVAL_TYPE_UNKNOWN;
}

View File

@ -290,6 +290,11 @@ class Compiler : public BaseCompiler
bool debugMode;
bool addTraceHints;
#ifdef JS_TYPE_INFERENCE
js::Vector<JSValueType, 16> argumentTypes;
js::Vector<JSValueType, 16> localTypes;
#endif
Compiler *thisFromCtor() { return this; }
public:
// Special atom index used to indicate that the atom is 'length'. This