mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
[INFER] Bytecode SSA analysis, bug 650715.
This commit is contained in:
parent
361316ea19
commit
7c7bcb48fd
1176
js/src/jsanalyze.cpp
1176
js/src/jsanalyze.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1492,7 +1492,7 @@ InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned co
|
||||
|
||||
TypeSet *types = type->getProperty(cx, JSID_VOID, true);
|
||||
if (!types)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (vector[i].isMagic(JS_ARRAY_HOLE))
|
||||
@ -1500,8 +1500,6 @@ InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned co
|
||||
jstype valtype = GetValueType(cx, vector[i]);
|
||||
types->addType(cx, valtype);
|
||||
}
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -131,8 +131,12 @@ JSCompartment::init(JSContext *cx)
|
||||
#ifdef JS_GCMETER
|
||||
memset(&compartmentStats, 0, sizeof(JSGCArenaStats) * FINALIZE_LIMIT);
|
||||
#endif
|
||||
|
||||
activeAnalysis = activeInference = false;
|
||||
types.init(cx);
|
||||
|
||||
JS_InitArenaPool(&pool, "analysis", 4096, 8, NULL);
|
||||
|
||||
if (!crossCompartmentWrappers.init())
|
||||
return false;
|
||||
|
||||
@ -495,7 +499,7 @@ void
|
||||
JSCompartment::markTypes(JSTracer *trc)
|
||||
{
|
||||
/* Mark all scripts and type objects in the compartment. */
|
||||
JS_ASSERT(types.inferenceDepth);
|
||||
JS_ASSERT(activeAnalysis);
|
||||
|
||||
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
|
||||
JSScript *script = reinterpret_cast<JSScript *>(cursor);
|
||||
@ -585,7 +589,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
||||
|
||||
#endif
|
||||
|
||||
if (!types.inferenceDepth && types.inferenceEnabled) {
|
||||
if (!activeAnalysis && types.inferenceEnabled) {
|
||||
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
|
||||
JSScript *script = reinterpret_cast<JSScript *>(cursor);
|
||||
script->condenseTypes(cx);
|
||||
@ -598,12 +602,12 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
||||
|
||||
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
|
||||
JSScript *script = reinterpret_cast<JSScript *>(cursor);
|
||||
script->sweepTypes(cx);
|
||||
script->sweepAnalysis(cx);
|
||||
}
|
||||
|
||||
if (!types.inferenceDepth) {
|
||||
/* Reset the inference pool, releasing all intermediate type data. */
|
||||
JS_FinishArenaPool(&types.pool);
|
||||
if (!activeAnalysis) {
|
||||
/* Reset the analysis pool, releasing all analysis and intermediate type data. */
|
||||
JS_FinishArenaPool(&pool);
|
||||
|
||||
/*
|
||||
* Destroy eval'ed scripts, now that any type inference information referring
|
||||
|
@ -387,6 +387,15 @@ struct JS_FRIEND_API(JSCompartment) {
|
||||
js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pool for analysis and intermediate type information in this compartment.
|
||||
* Cleared on every GC, unless the GC happens during analysis (indicated
|
||||
* by activeAnalysis, which is implied by activeInference).
|
||||
*/
|
||||
JSArenaPool pool;
|
||||
bool activeAnalysis;
|
||||
bool activeInference;
|
||||
|
||||
/* Type information about the scripts and objects in this compartment. */
|
||||
js::types::TypeCompartment types;
|
||||
|
||||
|
@ -1829,7 +1829,7 @@ MarkRuntime(JSTracer *trc)
|
||||
MarkContext(trc, acx);
|
||||
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
|
||||
if ((*c)->types.inferenceDepth)
|
||||
if ((*c)->activeAnalysis)
|
||||
(*c)->markTypes(trc);
|
||||
#ifdef JS_TRACER
|
||||
(*c)->traceMonitor.mark(trc);
|
||||
|
1113
js/src/jsinfer.cpp
1113
js/src/jsinfer.cpp
File diff suppressed because it is too large
Load Diff
@ -49,10 +49,7 @@
|
||||
|
||||
namespace js {
|
||||
struct CallArgs;
|
||||
namespace analyze {
|
||||
struct Bytecode;
|
||||
class Script;
|
||||
} }
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace types {
|
||||
@ -330,7 +327,7 @@ class TypeSet
|
||||
inline unsigned getObjectCount();
|
||||
inline TypeObject *getObject(unsigned i);
|
||||
|
||||
void setIntermediate() { typeFlags |= TYPE_FLAG_INTERMEDIATE_SET; }
|
||||
void setIntermediate() { JS_ASSERT(!typeFlags); typeFlags = TYPE_FLAG_INTERMEDIATE_SET; }
|
||||
void setOwnProperty(bool configurable) {
|
||||
typeFlags |= TYPE_FLAG_OWN_PROPERTY;
|
||||
if (configurable)
|
||||
@ -689,37 +686,6 @@ struct TypeResult
|
||||
TypeResult *next;
|
||||
};
|
||||
|
||||
/* Type information for a script, result of AnalyzeTypes. */
|
||||
struct TypeScript
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSScript *script;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Stack values pushed by all bytecodes in the script. Low bit is set for
|
||||
* bytecodes which are monitored (side effects were not determined statically).
|
||||
*/
|
||||
TypeSet **pushedArray;
|
||||
|
||||
/* Gather statistics off this script and print it if necessary. */
|
||||
void print(JSContext *cx, JSScript *script);
|
||||
|
||||
inline bool monitored(uint32 offset);
|
||||
inline void setMonitored(uint32 offset);
|
||||
|
||||
inline TypeSet *pushed(uint32 offset);
|
||||
inline TypeSet *pushed(uint32 offset, uint32 index);
|
||||
|
||||
inline void addType(JSContext *cx, uint32 offset, uint32 index, jstype type);
|
||||
};
|
||||
|
||||
/* Analyzes all types in script, constructing its TypeScript. */
|
||||
void AnalyzeScriptTypes(JSContext *cx, JSScript *script);
|
||||
|
||||
/* Analyze the effect of invoking 'new' on script. */
|
||||
void AnalyzeScriptNew(JSContext *cx, JSScript *script);
|
||||
|
||||
struct ArrayTableKey;
|
||||
typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
|
||||
|
||||
@ -736,12 +702,6 @@ struct TypeCompartment
|
||||
/* Whether type inference is enabled in this compartment. */
|
||||
bool inferenceEnabled;
|
||||
|
||||
/* Whether type inference is active, see AutoEnterTypeInference. */
|
||||
unsigned inferenceDepth;
|
||||
|
||||
/* Pool for all intermediate type information in this compartment. Cleared on every GC. */
|
||||
JSArenaPool pool;
|
||||
|
||||
/* Number of scripts in this compartment. */
|
||||
unsigned scriptCount;
|
||||
|
||||
@ -842,8 +802,6 @@ struct TypeCompartment
|
||||
bool dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval);
|
||||
bool dynamicCall(JSContext *cx, JSObject *callee, const CallArgs &args, bool constructing);
|
||||
|
||||
inline bool checkPendingRecompiles(JSContext *cx);
|
||||
|
||||
bool nukeTypes(JSContext *cx);
|
||||
bool processPendingRecompiles(JSContext *cx);
|
||||
|
||||
|
@ -143,50 +143,39 @@ TypeIdString(jsid id)
|
||||
struct AutoEnterTypeInference
|
||||
{
|
||||
JSContext *cx;
|
||||
#ifdef DEBUG
|
||||
unsigned depth;
|
||||
#endif
|
||||
bool oldActiveAnalysis;
|
||||
bool oldActiveInference;
|
||||
|
||||
AutoEnterTypeInference(JSContext *cx, bool compiling = false)
|
||||
: cx(cx)
|
||||
: cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
|
||||
oldActiveInference(cx->compartment->activeInference)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
depth = cx->compartment->types.inferenceDepth;
|
||||
#endif
|
||||
JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
|
||||
cx->compartment->types.inferenceDepth++;
|
||||
cx->compartment->activeAnalysis = true;
|
||||
cx->compartment->activeInference = true;
|
||||
}
|
||||
|
||||
~AutoEnterTypeInference()
|
||||
{
|
||||
cx->compartment->activeAnalysis = oldActiveAnalysis;
|
||||
cx->compartment->activeInference = oldActiveInference;
|
||||
|
||||
/*
|
||||
* This should have been reset by checkPendingRecompiles.
|
||||
* :FIXME: be more tolerant and clean up anyways, the caller may be
|
||||
* propagating an OOM or other error.
|
||||
* If there are no more type inference activations on the stack,
|
||||
* process any triggered recompilations. Note that we should not be
|
||||
* invoking any scripted code while type inference is running.
|
||||
* :TODO: assert this.
|
||||
*/
|
||||
JS_ASSERT(cx->compartment->types.inferenceDepth == depth);
|
||||
if (!cx->compartment->activeInference) {
|
||||
TypeCompartment *types = &cx->compartment->types;
|
||||
if (types->pendingNukeTypes)
|
||||
types->nukeTypes(cx);
|
||||
else if (types->pendingRecompiles)
|
||||
types->processPendingRecompiles(cx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
TypeCompartment::checkPendingRecompiles(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(inferenceDepth);
|
||||
if (--inferenceDepth != 0) {
|
||||
/*
|
||||
* There is still a type inference activation on the stack, wait for it to
|
||||
* finish before handling any recompilations. Note that we should not be
|
||||
* invoking any scripted code while the inference is running :TODO: assert this.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
if (pendingNukeTypes)
|
||||
return nukeTypes(cx);
|
||||
else if (pendingRecompiles && !processPendingRecompiles(cx))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure marking the currently compiled script, for constraints which can
|
||||
* trigger recompilation.
|
||||
@ -345,14 +334,14 @@ JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jst
|
||||
|
||||
js::types::TypeSet *types = obj->getProperty(this, id, true);
|
||||
if (!types || types->hasType(type))
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
|
||||
js::types::InferSpew(js::types::ISpewOps, "externalType: property %s %s: %s",
|
||||
obj->name(), js::types::TypeIdString(id),
|
||||
js::types::TypeString(type));
|
||||
types->addType(this, type);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -374,13 +363,13 @@ JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::Clo
|
||||
|
||||
js::types::TypeSet *types = obj->getProperty(this, id, true);
|
||||
if (!types)
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
|
||||
js::types::InferSpew(js::types::ISpewOps, "externalType: property %s %s",
|
||||
obj->name(), js::types::TypeIdString(id));
|
||||
types->addTypeSet(this, set);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline js::types::TypeObject *
|
||||
@ -408,7 +397,7 @@ JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid seco
|
||||
firstTypes->addBaseSubset(this, obj, secondTypes);
|
||||
secondTypes->addBaseSubset(this, obj, firstTypes);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -419,7 +408,7 @@ JSContext::addTypeFlags(js::types::TypeObject *obj, js::types::TypeObjectFlags f
|
||||
|
||||
js::types::AutoEnterTypeInference enter(this);
|
||||
obj->setFlags(this, flags);
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -456,7 +445,7 @@ JSContext::markTypePropertyConfigured(js::types::TypeObject *obj, jsid id)
|
||||
if (types)
|
||||
types->setOwnProperty(this, true);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -480,7 +469,7 @@ JSContext::markGlobalReallocation(JSObject *obj)
|
||||
}
|
||||
}
|
||||
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -491,7 +480,7 @@ JSContext::markTypeObjectUnknownProperties(js::types::TypeObject *obj)
|
||||
|
||||
js::types::AutoEnterTypeInference enter(this);
|
||||
obj->markUnknown(this);
|
||||
return compartment->types.checkPendingRecompiles(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -543,39 +532,48 @@ inline js::types::TypeSet *
|
||||
JSScript::returnTypes()
|
||||
{
|
||||
JS_ASSERT(varTypes);
|
||||
return &varTypes[0];
|
||||
return &varTypes[js::analyze::CalleeSlot()];
|
||||
}
|
||||
|
||||
inline js::types::TypeSet *
|
||||
JSScript::thisTypes()
|
||||
{
|
||||
JS_ASSERT(varTypes);
|
||||
return &varTypes[1];
|
||||
return &varTypes[js::analyze::ThisSlot()];
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: for non-escaping arguments and locals, argTypes/localTypes reflect
|
||||
* only the initial type of the variable (e.g. passed values for argTypes,
|
||||
* or undefined for localTypes) and not types from subsequent assignments.
|
||||
*/
|
||||
|
||||
inline js::types::TypeSet *
|
||||
JSScript::argTypes(unsigned i)
|
||||
{
|
||||
JS_ASSERT(varTypes && fun && i < fun->nargs);
|
||||
return &varTypes[2 + i];
|
||||
return &varTypes[js::analyze::ArgSlot(i)];
|
||||
}
|
||||
|
||||
inline js::types::TypeSet *
|
||||
JSScript::localTypes(unsigned i)
|
||||
{
|
||||
JS_ASSERT(varTypes && i < nfixed);
|
||||
if (fun)
|
||||
i += fun->nargs;
|
||||
return &varTypes[2 + i];
|
||||
return &varTypes[js::analyze::LocalSlot(this, i)];
|
||||
}
|
||||
|
||||
inline js::types::TypeSet *
|
||||
JSScript::upvarTypes(unsigned i)
|
||||
{
|
||||
JS_ASSERT(varTypes && i < bindings.countUpvars());
|
||||
if (fun)
|
||||
i += fun->nargs;
|
||||
return &varTypes[2 + nfixed + i];
|
||||
return &varTypes[js::analyze::LocalSlot(this, nfixed) + i];
|
||||
}
|
||||
|
||||
inline js::types::TypeSet *
|
||||
JSScript::slotTypes(unsigned slot)
|
||||
{
|
||||
JS_ASSERT(slot < js::analyze::TotalSlots(this));
|
||||
return &varTypes[slot];
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
@ -676,7 +674,8 @@ JSScript::typeSetThis(JSContext *cx, js::types::jstype type)
|
||||
return false;
|
||||
|
||||
/* Analyze the script regardless if -a was used. */
|
||||
bool analyze = !types && cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !isUncachedEval;
|
||||
bool analyze = !(analysis_ && analysis_->ranInference()) &&
|
||||
cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !isUncachedEval;
|
||||
|
||||
if (!thisTypes()->hasType(type) || analyze) {
|
||||
js::types::AutoEnterTypeInference enter(cx);
|
||||
@ -685,10 +684,14 @@ JSScript::typeSetThis(JSContext *cx, js::types::jstype type)
|
||||
id(), js::types::TypeString(type));
|
||||
thisTypes()->addType(cx, type);
|
||||
|
||||
if (analyze && !types)
|
||||
js::types::AnalyzeScriptTypes(cx, this);
|
||||
if (analyze && !(analysis_ && analysis_->ranInference())) {
|
||||
js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
|
||||
if (!analysis)
|
||||
return false;
|
||||
analysis->analyzeTypes(cx);
|
||||
}
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -712,7 +715,7 @@ JSScript::typeSetThis(JSContext *cx, js::types::ClonedTypeSet *set)
|
||||
js::types::InferSpew(js::types::ISpewOps, "externalType: setThis #%u", id());
|
||||
thisTypes()->addTypeSet(cx, set);
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -732,9 +735,10 @@ JSScript::typeSetNewCalled(JSContext *cx)
|
||||
if (analyzed) {
|
||||
/* Regenerate types for the function. */
|
||||
js::types::AutoEnterTypeInference enter(cx);
|
||||
js::types::AnalyzeScriptNew(cx, this);
|
||||
if (!cx->compartment->types.checkPendingRecompiles(cx))
|
||||
js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
|
||||
if (!analysis)
|
||||
return false;
|
||||
analysis->analyzeTypesNew(cx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -753,7 +757,7 @@ JSScript::typeSetLocal(JSContext *cx, unsigned local, js::types::jstype type)
|
||||
id(), local, js::types::TypeString(type));
|
||||
localTypes(local)->addType(cx, type);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -778,7 +782,7 @@ JSScript::typeSetLocal(JSContext *cx, unsigned local, js::types::ClonedTypeSet *
|
||||
js::types::InferSpew(js::types::ISpewOps, "externalType: setLocal #%u %u", id(), local);
|
||||
localTypes(local)->addTypeSet(cx, set);
|
||||
|
||||
return compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -795,7 +799,7 @@ JSScript::typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type)
|
||||
id(), arg, js::types::TypeString(type));
|
||||
argTypes(arg)->addType(cx, type);
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -820,7 +824,7 @@ JSScript::typeSetArgument(JSContext *cx, unsigned arg, js::types::ClonedTypeSet
|
||||
js::types::InferSpew(js::types::ISpewOps, "externalType: setArg #%u %u", id(), arg);
|
||||
argTypes(arg)->addTypeSet(cx, set);
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -838,7 +842,7 @@ JSScript::typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
|
||||
id(), upvar, js::types::TypeString(type));
|
||||
upvarTypes(upvar)->addType(cx, type);
|
||||
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -963,7 +967,7 @@ HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
|
||||
}
|
||||
|
||||
U **newValues = pool
|
||||
? ArenaArray<U*>(cx->compartment->types.pool, newCapacity)
|
||||
? ArenaArray<U*>(cx->compartment->pool, newCapacity)
|
||||
: (U **) js::OffTheBooks::malloc_(newCapacity * sizeof(U*));
|
||||
if (!newValues) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
@ -1010,7 +1014,7 @@ HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
|
||||
return (U **) &values;
|
||||
|
||||
values = pool
|
||||
? ArenaArray<U*>(cx->compartment->types.pool, SET_ARRAY_SIZE)
|
||||
? ArenaArray<U*>(cx->compartment->pool, SET_ARRAY_SIZE)
|
||||
: (U **) js::OffTheBooks::malloc_(SET_ARRAY_SIZE * sizeof(U*));
|
||||
if (!values) {
|
||||
values = (U **) oldData;
|
||||
@ -1119,7 +1123,7 @@ inline void
|
||||
TypeSet::addType(JSContext *cx, jstype type)
|
||||
{
|
||||
JS_ASSERT(type);
|
||||
JS_ASSERT(cx->compartment->types.inferenceDepth);
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
|
||||
if (unknown())
|
||||
return;
|
||||
@ -1208,9 +1212,9 @@ TypeSet::getObject(unsigned i)
|
||||
inline TypeSet *
|
||||
TypeSet::make(JSContext *cx, const char *name)
|
||||
{
|
||||
JS_ASSERT(cx->compartment->types.inferenceDepth);
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
|
||||
TypeSet *res = ArenaNew<TypeSet>(cx->compartment->types.pool);
|
||||
TypeSet *res = ArenaNew<TypeSet>(cx->compartment->pool);
|
||||
if (!res) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return NULL;
|
||||
@ -1233,7 +1237,7 @@ TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, const jsbytecode *pc
|
||||
thisTypes(NULL), thisType(0), returnTypes(NULL)
|
||||
{
|
||||
/* Caller must check for failure. */
|
||||
argumentTypes = ArenaArray<TypeSet*>(cx->compartment->types.pool, argumentCount);
|
||||
argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
|
||||
}
|
||||
|
||||
inline bool
|
||||
@ -1269,7 +1273,7 @@ TypeCallsite::compileAndGo()
|
||||
inline TypeSet *
|
||||
TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
|
||||
{
|
||||
JS_ASSERT(cx->compartment->types.inferenceDepth);
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
|
||||
JS_ASSERT_IF(JSID_IS_STRING(id), JSID_TO_STRING(id) != NULL);
|
||||
JS_ASSERT(!unknownProperties());
|
||||
@ -1304,46 +1308,9 @@ TypeObject::getProperty(unsigned i)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// TypeScript
|
||||
// TypeObject
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool
|
||||
TypeScript::monitored(uint32 offset)
|
||||
{
|
||||
JS_ASSERT(offset < script->length);
|
||||
return 0x1 & (size_t) pushedArray[offset];
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeScript::setMonitored(uint32 offset)
|
||||
{
|
||||
JS_ASSERT(script->compartment->types.inferenceDepth);
|
||||
JS_ASSERT(offset < script->length);
|
||||
pushedArray[offset] = (TypeSet *) (0x1 | (size_t) pushedArray[offset]);
|
||||
}
|
||||
|
||||
inline TypeSet *
|
||||
TypeScript::pushed(uint32 offset)
|
||||
{
|
||||
JS_ASSERT(offset < script->length);
|
||||
return (TypeSet *) (~0x1 & (size_t) pushedArray[offset]);
|
||||
}
|
||||
|
||||
inline TypeSet *
|
||||
TypeScript::pushed(uint32 offset, uint32 index)
|
||||
{
|
||||
JS_ASSERT(offset < script->length);
|
||||
JS_ASSERT(index < js::analyze::GetDefCount(script, offset));
|
||||
return pushed(offset) + index;
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeScript::addType(JSContext *cx, uint32 offset, uint32 index, jstype type)
|
||||
{
|
||||
TypeSet *types = pushed(offset, index);
|
||||
types->addType(cx, type);
|
||||
}
|
||||
|
||||
inline const char *
|
||||
TypeObject::name()
|
||||
{
|
||||
@ -1420,4 +1387,12 @@ class AutoTypeRooter : private AutoGCRooter {
|
||||
|
||||
} } /* namespace js::types */
|
||||
|
||||
inline void
|
||||
js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
|
||||
js::types::jstype type)
|
||||
{
|
||||
js::types::TypeSet *pushed = pushedTypes(offset, which);
|
||||
pushed->addType(cx, type);
|
||||
}
|
||||
|
||||
#endif // jsinferinlines_h___
|
||||
|
@ -1502,7 +1502,7 @@ DestroyScript(JSContext *cx, JSScript *script)
|
||||
PurgeScriptFragments(&script->compartment->traceMonitor, script);
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!script->types);
|
||||
JS_ASSERT(!script->hasAnalysis());
|
||||
|
||||
/* Migrate any type objects associated with this script to the compartment. */
|
||||
types::TypeObject *obj = script->typeObjects;
|
||||
|
@ -392,7 +392,7 @@ enum JITScriptStatus {
|
||||
|
||||
namespace js {
|
||||
namespace mjit { struct JITScript; }
|
||||
namespace analyze { class Script; }
|
||||
namespace analyze { class ScriptAnalysis; }
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -532,8 +532,19 @@ struct JSScript {
|
||||
/* Any possibly unexpected values pushed by opcodes in this script. */
|
||||
js::types::TypeResult *typeResults;
|
||||
|
||||
/* Results of type inference analysis for this script. Destroyed on GC. */
|
||||
js::types::TypeScript *types;
|
||||
/* Bytecode analysis and type inference results for this script. Destroyed on GC. */
|
||||
private:
|
||||
js::analyze::ScriptAnalysis *analysis_;
|
||||
void makeAnalysis(JSContext *cx);
|
||||
public:
|
||||
|
||||
bool hasAnalysis() { return analysis_ != NULL; }
|
||||
|
||||
js::analyze::ScriptAnalysis *analysis(JSContext *cx) {
|
||||
if (!analysis_)
|
||||
makeAnalysis(cx);
|
||||
return analysis_;
|
||||
}
|
||||
|
||||
inline JSObject *getGlobal();
|
||||
inline js::types::TypeObject *getGlobalType();
|
||||
@ -550,6 +561,9 @@ struct JSScript {
|
||||
inline js::types::TypeSet *localTypes(unsigned i);
|
||||
inline js::types::TypeSet *upvarTypes(unsigned i);
|
||||
|
||||
/* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
|
||||
inline js::types::TypeSet *slotTypes(unsigned slot);
|
||||
|
||||
private:
|
||||
bool makeVarTypes(JSContext *cx);
|
||||
public:
|
||||
@ -563,7 +577,7 @@ struct JSScript {
|
||||
inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
|
||||
|
||||
void condenseTypes(JSContext *cx);
|
||||
void sweepTypes(JSContext *cx);
|
||||
void sweepAnalysis(JSContext *cx);
|
||||
|
||||
/* Get a type object for an allocation site in this script. */
|
||||
inline js::types::TypeObject *
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -437,6 +437,18 @@ class Compiler : public BaseCompiler
|
||||
Label label;
|
||||
};
|
||||
|
||||
struct VarType {
|
||||
JSValueType type;
|
||||
types::TypeSet *types;
|
||||
};
|
||||
|
||||
struct SlotType
|
||||
{
|
||||
uint32 slot;
|
||||
VarType vt;
|
||||
SlotType(uint32 slot, VarType vt) : slot(slot), vt(vt) {}
|
||||
};
|
||||
|
||||
JSScript *outerScript;
|
||||
bool isConstructing;
|
||||
|
||||
@ -464,16 +476,13 @@ class Compiler : public BaseCompiler
|
||||
jsbytecode *parentPC;
|
||||
JSScript *script;
|
||||
uint32 inlineIndex;
|
||||
analyze::Script analysis;
|
||||
analyze::LifetimeScript liveness;
|
||||
Label *jumpMap;
|
||||
bool hasThisType;
|
||||
JSValueType thisType;
|
||||
JSValueType *argumentTypes;
|
||||
JSValueType *localTypes;
|
||||
uint32 depth;
|
||||
Vector<UnsyncedEntry> unsyncedEntries; // :XXX: handle OOM
|
||||
|
||||
/* Current types for non-escaping vars in the script. */
|
||||
VarType *varTypes;
|
||||
|
||||
/* State for managing return from inlined frames. */
|
||||
bool needReturnValue;
|
||||
bool syncReturnValue;
|
||||
@ -491,6 +500,7 @@ class Compiler : public BaseCompiler
|
||||
ActiveFrame *outer;
|
||||
|
||||
JSScript *script;
|
||||
analyze::ScriptAnalysis *analysis;
|
||||
jsbytecode *PC;
|
||||
bool variadicRejoin; /* There is a variadic rejoin for PC. */
|
||||
|
||||
@ -589,9 +599,6 @@ class Compiler : public BaseCompiler
|
||||
jsbytecode *inlinePC() { return PC; }
|
||||
uint32 inlineIndex() { return a->inlineIndex; }
|
||||
|
||||
types::TypeSet *getTypeSet(uint32 slot);
|
||||
types::TypeSet *getTypeSet(const FrameEntry *fe) { return getTypeSet(frame.indexOfFe(fe)); }
|
||||
|
||||
Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; }
|
||||
|
||||
InvariantCodePatch *getInvariantPatch(unsigned index, bool call) {
|
||||
@ -609,23 +616,17 @@ class Compiler : public BaseCompiler
|
||||
CompileStatus finishThisUp(JITScript **jitp);
|
||||
CompileStatus pushActiveFrame(JSScript *script, uint32 argc);
|
||||
void popActiveFrame();
|
||||
void generateInlinePrologue();
|
||||
|
||||
/* Analysis helpers. */
|
||||
CompileStatus prepareInferenceTypes(JSScript *script, ActiveFrame *a);
|
||||
inline bool preserveLocalType(unsigned i);
|
||||
inline bool preserveArgType(unsigned i);
|
||||
void fixDoubleTypes();
|
||||
void restoreAnalysisTypes(uint32 stackDepth);
|
||||
inline bool fixDoubleSlot(uint32 slot);
|
||||
void fixDoubleTypes(jsbytecode *target);
|
||||
void restoreAnalysisTypes();
|
||||
void watchGlobalReallocation();
|
||||
JSValueType knownThisType();
|
||||
JSValueType knownArgumentType(uint32 arg);
|
||||
JSValueType knownLocalType(uint32 local);
|
||||
void updateVarType();
|
||||
JSValueType knownPushedType(uint32 pushed);
|
||||
bool arrayPrototypeHasIndexedProperty();
|
||||
bool mayPushUndefined(uint32 pushed);
|
||||
types::TypeSet *argTypeSet(uint32 arg);
|
||||
types::TypeSet *localTypeSet(uint32 local);
|
||||
types::TypeSet *pushedTypeSet(uint32 which);
|
||||
bool monitored(jsbytecode *pc);
|
||||
bool testSingletonProperty(JSObject *obj, jsid id);
|
||||
|
@ -1114,7 +1114,7 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, AutoRejoinSite
|
||||
* Sync everything except the top two entries.
|
||||
* We will handle the lhs/rhs in the stub call path.
|
||||
*/
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameSlots()), Uses(2));
|
||||
|
||||
RegisterID tempReg = frame.allocReg();
|
||||
@ -1389,7 +1389,7 @@ mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, AutoRejoinSite &a
|
||||
FrameEntry *lhs = frame.peek(-2);
|
||||
|
||||
if (target)
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
|
||||
JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
|
||||
|
||||
@ -1487,7 +1487,7 @@ mjit::Compiler::jsop_relational_int(JSOp op, AutoRejoinSite &autoRejoin, jsbytec
|
||||
Assembler::Condition cond = GetCompareCondition(op, fused);
|
||||
|
||||
if (target) {
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
if (!frame.syncForBranch(target, Uses(2)))
|
||||
return false;
|
||||
|
||||
@ -1538,7 +1538,7 @@ mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, AutoRejoinSite &rej
|
||||
FrameEntry *lhs = frame.peek(-2);
|
||||
|
||||
if (target)
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
|
||||
/* Allocate all registers up-front. */
|
||||
FrameState::BinaryAlloc regs;
|
||||
|
@ -430,7 +430,7 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, AutoRejoinSite &autoRejoin
|
||||
*/
|
||||
|
||||
if (target) {
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
frame.syncAndKillEverything();
|
||||
frame.freeReg(reg);
|
||||
|
||||
@ -498,7 +498,7 @@ mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, AutoRejoinSite &autoRejoin
|
||||
frame.forgetMismatchedObject(rhs);
|
||||
Assembler::Condition cond = GetCompareCondition(op, fused);
|
||||
if (target) {
|
||||
fixDoubleTypes();
|
||||
fixDoubleTypes(target);
|
||||
autoRejoin.oolRejoin(stubcc.masm.label());
|
||||
Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
|
||||
Registers::ReturnReg, Registers::ReturnReg);
|
||||
@ -873,6 +873,7 @@ mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target)
|
||||
bool
|
||||
mjit::Compiler::jsop_ifneq(JSOp op, jsbytecode *target)
|
||||
{
|
||||
fixDoubleTypes(target);
|
||||
FrameEntry *fe = frame.peek(-1);
|
||||
|
||||
if (fe->isConstant()) {
|
||||
@ -900,6 +901,7 @@ mjit::Compiler::jsop_ifneq(JSOp op, jsbytecode *target)
|
||||
bool
|
||||
mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target)
|
||||
{
|
||||
fixDoubleTypes(target);
|
||||
FrameEntry *fe = frame.peek(-1);
|
||||
|
||||
if (fe->isConstant()) {
|
||||
@ -908,7 +910,6 @@ mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target)
|
||||
/* Short-circuit. */
|
||||
if ((op == JSOP_OR && b == JS_TRUE) ||
|
||||
(op == JSOP_AND && b == JS_FALSE)) {
|
||||
fixDoubleTypes();
|
||||
if (!frame.syncForBranch(target, Uses(0)))
|
||||
return false;
|
||||
if (!jumpAndTrace(masm.jump(), target))
|
||||
@ -925,14 +926,17 @@ mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target)
|
||||
bool
|
||||
mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
|
||||
{
|
||||
JSValueType type = knownLocalType(slot);
|
||||
updateVarType();
|
||||
|
||||
types::TypeSet *types = pushedTypeSet(0);
|
||||
JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
|
||||
|
||||
if (popped || (op == JSOP_INCLOCAL || op == JSOP_DECLOCAL)) {
|
||||
int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? -1 : 1;
|
||||
|
||||
// Before:
|
||||
// After: V
|
||||
frame.pushLocal(slot, type);
|
||||
frame.pushLocal(slot);
|
||||
|
||||
// Before: V
|
||||
// After: V 1
|
||||
@ -941,12 +945,12 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
|
||||
// Note, SUB will perform integer conversion for us.
|
||||
// Before: V 1
|
||||
// After: N+1
|
||||
if (!jsop_binary(JSOP_SUB, stubs::Sub, type, localTypeSet(slot)))
|
||||
if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
|
||||
return false;
|
||||
|
||||
// Before: N+1
|
||||
// After: N+1
|
||||
frame.storeLocal(slot, type, popped, true);
|
||||
frame.storeLocal(slot, popped, true);
|
||||
|
||||
if (popped)
|
||||
frame.pop();
|
||||
@ -955,7 +959,7 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
|
||||
|
||||
// Before:
|
||||
// After: V
|
||||
frame.pushLocal(slot, type);
|
||||
frame.pushLocal(slot);
|
||||
|
||||
// Before: V
|
||||
// After: N
|
||||
@ -971,12 +975,12 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
|
||||
|
||||
// Before: N N 1
|
||||
// After: N N+1
|
||||
if (!jsop_binary(JSOP_ADD, stubs::Add, type, localTypeSet(slot)))
|
||||
if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
|
||||
return false;
|
||||
|
||||
// Before: N N+1
|
||||
// After: N N+1
|
||||
frame.storeLocal(slot, type, true, true);
|
||||
frame.storeLocal(slot, true, true);
|
||||
|
||||
// Before: N N+1
|
||||
// After: N
|
||||
@ -989,14 +993,17 @@ mjit::Compiler::jsop_localinc(JSOp op, uint32 slot, bool popped)
|
||||
bool
|
||||
mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped)
|
||||
{
|
||||
JSValueType type = knownArgumentType(slot);
|
||||
updateVarType();
|
||||
|
||||
types::TypeSet *types = pushedTypeSet(0);
|
||||
JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
|
||||
|
||||
if (popped || (op == JSOP_INCARG || op == JSOP_DECARG)) {
|
||||
int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? -1 : 1;
|
||||
|
||||
// Before:
|
||||
// After: V
|
||||
frame.pushArg(slot, type);
|
||||
frame.pushArg(slot);
|
||||
|
||||
// Before: V
|
||||
// After: V 1
|
||||
@ -1005,12 +1012,12 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped)
|
||||
// Note, SUB will perform integer conversion for us.
|
||||
// Before: V 1
|
||||
// After: N+1
|
||||
if (!jsop_binary(JSOP_SUB, stubs::Sub, type, argTypeSet(slot)))
|
||||
if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
|
||||
return false;
|
||||
|
||||
// Before: N+1
|
||||
// After: N+1
|
||||
frame.storeArg(slot, type, popped);
|
||||
frame.storeArg(slot, popped);
|
||||
|
||||
if (popped)
|
||||
frame.pop();
|
||||
@ -1019,7 +1026,7 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped)
|
||||
|
||||
// Before:
|
||||
// After: V
|
||||
frame.pushArg(slot, type);
|
||||
frame.pushArg(slot);
|
||||
|
||||
// Before: V
|
||||
// After: N
|
||||
@ -1035,12 +1042,12 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped)
|
||||
|
||||
// Before: N N 1
|
||||
// After: N N+1
|
||||
if (!jsop_binary(JSOP_ADD, stubs::Add, type, argTypeSet(slot)))
|
||||
if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
|
||||
return false;
|
||||
|
||||
// Before: N N+1
|
||||
// After: N N+1
|
||||
frame.storeArg(slot, type, true);
|
||||
frame.storeArg(slot, true);
|
||||
|
||||
// Before: N N+1
|
||||
// After: N
|
||||
@ -1109,7 +1116,8 @@ mjit::Compiler::jsop_setelem_dense()
|
||||
// hoist the initialized length check, we make the slots pointer loop
|
||||
// invariant and never access the object itself.
|
||||
RegisterID slotsReg;
|
||||
bool hoisted = loop && !a->parent && loop->hoistArrayLengthCheck(obj, 1);
|
||||
bool hoisted = loop && !a->parent &&
|
||||
loop->hoistArrayLengthCheck(obj, frame.extra(obj).types, 1);
|
||||
|
||||
if (hoisted) {
|
||||
FrameEntry *slotsFe = loop->invariantSlots(obj);
|
||||
@ -1433,7 +1441,8 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
|
||||
// We checked in the caller that prototypes do not have indexed properties.
|
||||
bool allowUndefined = mayPushUndefined(0);
|
||||
|
||||
bool hoisted = loop && !a->parent && loop->hoistArrayLengthCheck(obj, 0);
|
||||
bool hoisted = loop && !a->parent &&
|
||||
loop->hoistArrayLengthCheck(obj, frame.extra(obj).types, 0);
|
||||
|
||||
// Get a register with either the object or its slots, depending on whether
|
||||
// we are hoisting the bounds check.
|
||||
|
@ -1187,10 +1187,10 @@ FrameState::syncAt(int32 n)
|
||||
}
|
||||
|
||||
inline void
|
||||
FrameState::pushLocal(uint32 n, JSValueType knownType)
|
||||
FrameState::pushLocal(uint32 n)
|
||||
{
|
||||
FrameEntry *fe = getLocal(n);
|
||||
if (!a->analysis->localEscapes(n)) {
|
||||
if (!analysis->slotEscapes(analyze::LocalSlot(script, n))) {
|
||||
pushCopyOf(indexOfFe(fe));
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
@ -1199,29 +1199,27 @@ FrameState::pushLocal(uint32 n, JSValueType knownType)
|
||||
* SETLOCAL equivocation of stack slots, and let expressions, just
|
||||
* weakly assert on the fixed local vars.
|
||||
*/
|
||||
FrameEntry *fe = &locals[n];
|
||||
if (fe->isTracked() && n < script->nfixed) {
|
||||
JS_ASSERT(fe->type.inMemory());
|
||||
if (fe->isTracked() && n < script->nfixed)
|
||||
JS_ASSERT(fe->data.inMemory());
|
||||
}
|
||||
#endif
|
||||
push(addressOf(fe), knownType);
|
||||
JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
|
||||
push(addressOf(fe), type);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
FrameState::pushArg(uint32 n, JSValueType knownType)
|
||||
FrameState::pushArg(uint32 n)
|
||||
{
|
||||
FrameEntry *fe = getArg(n);
|
||||
if (!a->analysis->argEscapes(n)) {
|
||||
if (!analysis->slotEscapes(analyze::ArgSlot(n))) {
|
||||
pushCopyOf(indexOfFe(fe));
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
FrameEntry *fe = &args[n];
|
||||
if (fe->isTracked())
|
||||
JS_ASSERT(fe->data.inMemory());
|
||||
#endif
|
||||
push(addressOf(fe), knownType);
|
||||
JSValueType type = fe->isTypeKnown() ? fe->getKnownType() : JSVAL_TYPE_UNKNOWN;
|
||||
push(addressOf(fe), type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@ FrameState::~FrameState()
|
||||
{
|
||||
while (a) {
|
||||
ActiveFrame *parent = a->parent;
|
||||
a->script->analysis(cx)->clearAllocations();
|
||||
#if defined JS_NUNBOX32
|
||||
a->reifier.~ImmutableSync();
|
||||
#endif
|
||||
@ -115,8 +116,7 @@ FrameState::getUnsyncedEntries(uint32 *pdepth, Vector<UnsyncedEntry> *unsyncedEn
|
||||
}
|
||||
|
||||
bool
|
||||
FrameState::pushActiveFrame(JSScript *script, uint32 argc,
|
||||
analyze::Script *analysis, analyze::LifetimeScript *liveness)
|
||||
FrameState::pushActiveFrame(JSScript *script, uint32 argc)
|
||||
{
|
||||
uint32 depth = a ? totalDepth() : 0;
|
||||
uint32 nentries = feLimit(script);
|
||||
@ -147,9 +147,6 @@ FrameState::pushActiveFrame(JSScript *script, uint32 argc,
|
||||
newa->script = script;
|
||||
newa->freeRegs = Registers(Registers::AvailAnyRegs);
|
||||
|
||||
newa->analysis = analysis;
|
||||
newa->liveness = liveness;
|
||||
|
||||
newa->entries = (FrameEntry *)cursor;
|
||||
cursor += sizeof(FrameEntry) * nentries;
|
||||
|
||||
@ -169,7 +166,7 @@ FrameState::pushActiveFrame(JSScript *script, uint32 argc,
|
||||
this->a = newa;
|
||||
updateActiveFrame();
|
||||
|
||||
if (a->parent && a->analysis->inlineable(argc)) {
|
||||
if (a->parent && script->analysis(cx)->inlineable(argc)) {
|
||||
a->depth = depth + VALUES_PER_STACK_FRAME;
|
||||
|
||||
/* Mark all registers which are in use by the parent or its own parent. */
|
||||
@ -257,6 +254,8 @@ FrameState::popActiveFrame()
|
||||
FrameEntry *parentSP = a->parentSP;
|
||||
ActiveFrame *parent = a->parent;
|
||||
|
||||
analysis->clearAllocations();
|
||||
|
||||
#if defined JS_NUNBOX32
|
||||
a->reifier.~ImmutableSync();
|
||||
#endif
|
||||
@ -272,6 +271,7 @@ void
|
||||
FrameState::updateActiveFrame()
|
||||
{
|
||||
script = a->script;
|
||||
analysis = script->analysis(cx);
|
||||
entries = a->entries;
|
||||
callee_ = a->callee_;
|
||||
this_ = a->this_;
|
||||
@ -464,7 +464,7 @@ FrameState::variableLive(FrameEntry *fe, jsbytecode *pc) const
|
||||
JS_ASSERT(fe < spBase && fe != callee_);
|
||||
|
||||
uint32 offset = pc - script->code;
|
||||
return a->liveness->live(indexOfFe(fe), offset);
|
||||
return analysis->liveness(indexOfFe(fe)).live(offset);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -713,11 +713,11 @@ RegisterAllocation *
|
||||
FrameState::computeAllocation(jsbytecode *target)
|
||||
{
|
||||
JS_ASSERT(cx->typeInferenceEnabled());
|
||||
RegisterAllocation *alloc = ArenaNew<RegisterAllocation>(a->liveness->pool, false);
|
||||
RegisterAllocation *alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
|
||||
if (!alloc)
|
||||
return NULL;
|
||||
|
||||
if (a->analysis->getCode(target).exceptionEntry || a->analysis->getCode(target).switchTarget ||
|
||||
if (analysis->getCode(target).exceptionEntry || analysis->getCode(target).switchTarget ||
|
||||
JSOp(*target) == JSOP_TRAP) {
|
||||
/* State must be synced at exception and switch targets, and at traps. */
|
||||
#ifdef DEBUG
|
||||
@ -821,7 +821,7 @@ FrameState::syncForBranch(jsbytecode *target, Uses uses)
|
||||
|
||||
Registers regs = 0;
|
||||
|
||||
RegisterAllocation *&alloc = a->liveness->getCode(target).allocation;
|
||||
RegisterAllocation *&alloc = analysis->getAllocation(target);
|
||||
if (!alloc) {
|
||||
alloc = computeAllocation(target);
|
||||
if (!alloc)
|
||||
@ -955,14 +955,14 @@ FrameState::discardForJoin(jsbytecode *target, uint32 stackDepth)
|
||||
return true;
|
||||
}
|
||||
|
||||
RegisterAllocation *&alloc = a->liveness->getCode(target).allocation;
|
||||
RegisterAllocation *&alloc = analysis->getAllocation(target);
|
||||
|
||||
if (!alloc) {
|
||||
/*
|
||||
* This shows up for loop entries which are not reachable from the
|
||||
* loop head, and for exception, switch target and trap safe points.
|
||||
*/
|
||||
alloc = ArenaNew<RegisterAllocation>(a->liveness->pool, false);
|
||||
alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, false);
|
||||
if (!alloc)
|
||||
return false;
|
||||
}
|
||||
@ -1019,7 +1019,7 @@ FrameState::consistentRegisters(jsbytecode *target)
|
||||
* which is not consistent with the target's register state has already
|
||||
* been synced, and no stores will need to be issued by prepareForJump.
|
||||
*/
|
||||
RegisterAllocation *alloc = a->liveness->getCode(target).allocation;
|
||||
RegisterAllocation *alloc = analysis->getAllocation(target);
|
||||
JS_ASSERT(alloc);
|
||||
|
||||
Registers regs(Registers::AvailAnyRegs);
|
||||
@ -1051,7 +1051,7 @@ FrameState::prepareForJump(jsbytecode *target, Assembler &masm, bool synced)
|
||||
|
||||
JS_ASSERT_IF(!synced, !consistentRegisters(target));
|
||||
|
||||
RegisterAllocation *alloc = a->liveness->getCode(target).allocation;
|
||||
RegisterAllocation *alloc = analysis->getAllocation(target);
|
||||
JS_ASSERT(alloc);
|
||||
|
||||
Registers regs = 0;
|
||||
@ -2306,58 +2306,43 @@ FrameState::separateBinaryEntries(FrameEntry *lhs, FrameEntry *rhs)
|
||||
}
|
||||
|
||||
void
|
||||
FrameState::storeLocal(uint32 n, JSValueType type, bool popGuaranteed, bool fixedType)
|
||||
FrameState::storeLocal(uint32 n, bool popGuaranteed, bool fixedType)
|
||||
{
|
||||
FrameEntry *local = getLocal(n);
|
||||
|
||||
if (a->analysis->localEscapes(n)) {
|
||||
if (analysis->slotEscapes(indexOfFe(local))) {
|
||||
JS_ASSERT(local->data.inMemory());
|
||||
storeTo(peek(-1), addressOf(local), popGuaranteed);
|
||||
return;
|
||||
}
|
||||
|
||||
storeTop(local, type, popGuaranteed);
|
||||
storeTop(local, popGuaranteed);
|
||||
|
||||
if (loop)
|
||||
local->lastLoop = loop->headOffset();
|
||||
|
||||
if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE &&
|
||||
fixedType && !local->type.synced()) {
|
||||
/*
|
||||
* Except when inlining, known types are always in sync for locals.
|
||||
* If we are inlining, the known type is filled in when the frame is
|
||||
* expanded (which happens upon any recompilation activity).
|
||||
*/
|
||||
local->type.sync();
|
||||
}
|
||||
|
||||
if (inTryBlock)
|
||||
syncFe(local);
|
||||
}
|
||||
|
||||
void
|
||||
FrameState::storeArg(uint32 n, JSValueType type, bool popGuaranteed)
|
||||
FrameState::storeArg(uint32 n, bool popGuaranteed)
|
||||
{
|
||||
// Note that args are always immediately synced, because they can be
|
||||
// aliased (but not written to) via f.arguments.
|
||||
FrameEntry *arg = getArg(n);
|
||||
|
||||
if (a->analysis->argEscapes(n)) {
|
||||
if (analysis->slotEscapes(indexOfFe(arg))) {
|
||||
JS_ASSERT(arg->data.inMemory());
|
||||
storeTo(peek(-1), addressOf(arg), popGuaranteed);
|
||||
return;
|
||||
}
|
||||
|
||||
storeTop(arg, type, popGuaranteed);
|
||||
storeTop(arg, popGuaranteed);
|
||||
|
||||
if (loop)
|
||||
arg->lastLoop = loop->headOffset();
|
||||
|
||||
if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE && !arg->type.synced()) {
|
||||
/* Known types are always in sync for args. (Frames which update args are not inlined). */
|
||||
arg->type.sync();
|
||||
}
|
||||
|
||||
syncFe(arg);
|
||||
}
|
||||
|
||||
@ -2377,7 +2362,7 @@ FrameState::forgetEntry(FrameEntry *fe)
|
||||
}
|
||||
|
||||
void
|
||||
FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
FrameState::storeTop(FrameEntry *target, bool popGuaranteed)
|
||||
{
|
||||
JS_ASSERT(!isTemporary(target));
|
||||
|
||||
@ -2388,6 +2373,15 @@ FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is overwriting a known non-double type with another value of the
|
||||
* same type, then make sure we keep the type marked as synced after doing
|
||||
* the copy.
|
||||
*/
|
||||
bool wasSynced = target->type.synced();
|
||||
JSValueType oldType = target->isTypeKnown() ? target->getKnownType() : JSVAL_TYPE_UNKNOWN;
|
||||
bool trySyncType = wasSynced && oldType != JSVAL_TYPE_UNKNOWN && oldType != JSVAL_TYPE_DOUBLE;
|
||||
|
||||
/* Completely invalidate the local variable. */
|
||||
forgetEntry(target);
|
||||
target->resetUnsynced();
|
||||
@ -2397,6 +2391,8 @@ FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
target->setCopyOf(NULL);
|
||||
target->setNotCopied();
|
||||
target->setConstant(Jsvalify(top->getValue()));
|
||||
if (trySyncType && target->isType(oldType))
|
||||
target->type.sync();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2425,6 +2421,8 @@ FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
swapInTracker(backing, target);
|
||||
target->setNotCopied();
|
||||
target->setCopyOf(backing);
|
||||
if (trySyncType && target->isType(oldType))
|
||||
target->type.sync();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2468,18 +2466,9 @@ FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
|
||||
if (backing->isType(JSVAL_TYPE_DOUBLE)) {
|
||||
FPRegisterID fpreg = tempFPRegForData(backing);
|
||||
if (type != JSVAL_TYPE_DOUBLE) {
|
||||
masm.storeDouble(fpreg, addressOf(target));
|
||||
target->resetSynced();
|
||||
|
||||
/* We're about to invalidate the backing, so forget the FP register. */
|
||||
forgetReg(fpreg);
|
||||
} else {
|
||||
target->data.setFPRegister(fpreg);
|
||||
regstate(fpreg).reassociate(target);
|
||||
}
|
||||
|
||||
target->setType(JSVAL_TYPE_DOUBLE);
|
||||
target->data.setFPRegister(fpreg);
|
||||
regstate(fpreg).reassociate(target);
|
||||
} else {
|
||||
/*
|
||||
* Move the backing store down - we spill registers here, but we could be
|
||||
@ -2495,44 +2484,23 @@ FrameState::storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed)
|
||||
target->data.setRegister(reg);
|
||||
regstate(reg).reassociate(target);
|
||||
|
||||
if (type == JSVAL_TYPE_UNKNOWN) {
|
||||
if (backing->isTypeKnown()) {
|
||||
target->setType(backing->getKnownType());
|
||||
} else {
|
||||
pinReg(reg);
|
||||
RegisterID typeReg = tempRegForType(backing);
|
||||
unpinReg(reg);
|
||||
target->type.setRegister(typeReg);
|
||||
regstate(typeReg).reassociate(target);
|
||||
}
|
||||
} else if (type != JSVAL_TYPE_DOUBLE || backing->isType(JSVAL_TYPE_INT32)) {
|
||||
/*
|
||||
* Treat the stored entry as an int even if inference has marked it
|
||||
* as a float (we will fixDoubles on it before branching), to avoid
|
||||
* demoting the backing.
|
||||
*/
|
||||
if (type == JSVAL_TYPE_DOUBLE)
|
||||
type = JSVAL_TYPE_INT32;
|
||||
JS_ASSERT_IF(backing->isTypeKnown(), backing->isType(type));
|
||||
if (!backing->isTypeKnown())
|
||||
learnType(backing, type);
|
||||
target->setType(type);
|
||||
if (backing->isTypeKnown()) {
|
||||
target->setType(backing->getKnownType());
|
||||
} else {
|
||||
FPRegisterID fpreg = allocFPReg();
|
||||
syncFe(backing);
|
||||
masm.moveInt32OrDouble(addressOf(backing), fpreg);
|
||||
|
||||
forgetAllRegs(backing);
|
||||
backing->setType(JSVAL_TYPE_DOUBLE);
|
||||
target->setType(JSVAL_TYPE_DOUBLE);
|
||||
target->data.setFPRegister(fpreg);
|
||||
regstate(fpreg).associate(target, RematInfo::DATA);
|
||||
pinReg(reg);
|
||||
RegisterID typeReg = tempRegForType(backing);
|
||||
unpinReg(reg);
|
||||
target->type.setRegister(typeReg);
|
||||
regstate(typeReg).reassociate(target);
|
||||
}
|
||||
}
|
||||
|
||||
backing->setCopyOf(target);
|
||||
JS_ASSERT(top->copyOf() == target);
|
||||
|
||||
if (trySyncType && target->isType(oldType))
|
||||
target->type.sync();
|
||||
|
||||
/*
|
||||
* Right now, |backing| is a copy of |target| (note the reversal), but
|
||||
* |target| is not marked as copied. This is an optimization so uncopy()
|
||||
@ -2551,7 +2519,7 @@ FrameState::shimmy(uint32 n)
|
||||
{
|
||||
JS_ASSERT(sp - n >= spBase);
|
||||
int32 depth = 0 - int32(n);
|
||||
storeTop(peek(depth - 1), JSVAL_TYPE_UNKNOWN, true);
|
||||
storeTop(peek(depth - 1), true);
|
||||
popn(n);
|
||||
}
|
||||
|
||||
@ -2560,7 +2528,7 @@ FrameState::shift(int32 n)
|
||||
{
|
||||
JS_ASSERT(n < 0);
|
||||
JS_ASSERT(sp + n - 1 >= spBase);
|
||||
storeTop(peek(n - 1), JSVAL_TYPE_UNKNOWN, true);
|
||||
storeTop(peek(n - 1), true);
|
||||
pop();
|
||||
}
|
||||
|
||||
|
@ -368,8 +368,8 @@ class FrameState
|
||||
|
||||
// Pushes a copy of a slot (formal argument, local variable, or stack slot)
|
||||
// onto the operation stack.
|
||||
void pushLocal(uint32 n, JSValueType knownType);
|
||||
void pushArg(uint32 n, JSValueType knownType);
|
||||
void pushLocal(uint32 n);
|
||||
void pushArg(uint32 n);
|
||||
void pushCallee();
|
||||
void pushThis();
|
||||
void pushTemporary(FrameEntry *fe);
|
||||
@ -603,13 +603,10 @@ class FrameState
|
||||
void loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
|
||||
void loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg);
|
||||
|
||||
/*
|
||||
* Stores the top stack slot back to a local or slot. type indicates any known
|
||||
* type for the local/slot.
|
||||
*/
|
||||
void storeLocal(uint32 n, JSValueType type, bool popGuaranteed = false, bool fixedType = false);
|
||||
void storeArg(uint32 n, JSValueType type, bool popGuaranteed = false);
|
||||
void storeTop(FrameEntry *target, JSValueType type, bool popGuaranteed);
|
||||
/* Stores the top stack slot back to a local or slot. */
|
||||
void storeLocal(uint32 n, bool popGuaranteed = false, bool fixedType = false);
|
||||
void storeArg(uint32 n, bool popGuaranteed = false);
|
||||
void storeTop(FrameEntry *target, bool popGuaranteed);
|
||||
|
||||
/*
|
||||
* Restores state from a slow path.
|
||||
@ -872,8 +869,7 @@ class FrameState
|
||||
|
||||
void getUnsyncedEntries(uint32 *pdepth, Vector<UnsyncedEntry> *unsyncedEntries);
|
||||
|
||||
bool pushActiveFrame(JSScript *script, uint32 argc,
|
||||
analyze::Script *analysis, analyze::LifetimeScript *liveness);
|
||||
bool pushActiveFrame(JSScript *script, uint32 argc);
|
||||
void popActiveFrame();
|
||||
|
||||
void discardLocalRegisters();
|
||||
@ -1077,15 +1073,13 @@ class FrameState
|
||||
#if defined JS_NUNBOX32
|
||||
mutable ImmutableSync reifier;
|
||||
#endif
|
||||
|
||||
analyze::Script *analysis;
|
||||
analyze::LifetimeScript *liveness;
|
||||
};
|
||||
ActiveFrame *a;
|
||||
|
||||
/* State derived/copied from the active frame. :XXX: remove? */
|
||||
|
||||
JSScript *script;
|
||||
analyze::ScriptAnalysis *analysis;
|
||||
|
||||
FrameEntry *entries;
|
||||
FrameEntry *callee_;
|
||||
|
@ -381,8 +381,6 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint
|
||||
if (!newscript->typeSetArgument(cx, i, &argTypes[1 + i]))
|
||||
return false;
|
||||
}
|
||||
if (!cx->compartment->types.checkPendingRecompiles(cx))
|
||||
return false;
|
||||
} else {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!cx->typeMonitorCall(args, flags & JSFRAME_CONSTRUCTING))
|
||||
|
@ -46,9 +46,8 @@ using namespace js::mjit;
|
||||
using namespace js::analyze;
|
||||
|
||||
LoopState::LoopState(JSContext *cx, JSScript *script,
|
||||
mjit::Compiler *cc, FrameState *frame,
|
||||
Script *analysis, LifetimeScript *liveness)
|
||||
: cx(cx), script(script), cc(*cc), frame(*frame), analysis(analysis), liveness(liveness),
|
||||
mjit::Compiler *cc, FrameState *frame)
|
||||
: cx(cx), script(script), analysis(script->analysis(cx)), cc(*cc), frame(*frame),
|
||||
lifetime(NULL), alloc(NULL), loopRegs(0), skipAnalysis(false),
|
||||
loopJoins(CompilerAllocPolicy(cx, *cc)),
|
||||
loopPatches(CompilerAllocPolicy(cx, *cc)),
|
||||
@ -67,18 +66,13 @@ LoopState::LoopState(JSContext *cx, JSScript *script,
|
||||
bool
|
||||
LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
|
||||
{
|
||||
this->lifetime = liveness->getCode(head).loop;
|
||||
this->lifetime = analysis->getLoop(head);
|
||||
JS_ASSERT(lifetime &&
|
||||
lifetime->head == uint32(head - script->code) &&
|
||||
lifetime->entry == uint32(entryTarget - script->code));
|
||||
|
||||
this->entry = entry;
|
||||
|
||||
if (!stack.analyze(liveness->pool, script, lifetime->head,
|
||||
lifetime->backedge - lifetime->head + 1, analysis)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
analyzeLoopTest();
|
||||
analyzeLoopIncrements();
|
||||
analyzeModset();
|
||||
@ -109,10 +103,10 @@ LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
|
||||
types::TypeIdString(modifiedProperties[i].id));
|
||||
}
|
||||
|
||||
RegisterAllocation *&alloc = liveness->getCode(head).allocation;
|
||||
RegisterAllocation *&alloc = analysis->getAllocation(head);
|
||||
JS_ASSERT(!alloc);
|
||||
|
||||
alloc = ArenaNew<RegisterAllocation>(liveness->pool, true);
|
||||
alloc = ArenaNew<RegisterAllocation>(cx->compartment->pool, true);
|
||||
if (!alloc)
|
||||
return false;
|
||||
|
||||
@ -242,26 +236,9 @@ LoopState::clearLoopRegisters()
|
||||
bool
|
||||
LoopState::loopInvariantEntry(uint32 slot)
|
||||
{
|
||||
unsigned nargs = script->fun ? script->fun->nargs : 0;
|
||||
|
||||
if (slot >= 2 + nargs + script->nfixed)
|
||||
if (slot == analyze::CalleeSlot() || analysis->slotEscapes(slot))
|
||||
return false;
|
||||
|
||||
if (liveness->firstWrite(slot, lifetime) != uint32(-1))
|
||||
return false;
|
||||
|
||||
if (slot == 0) /* callee */
|
||||
return false;
|
||||
if (slot == 1) /* this */
|
||||
return true;
|
||||
slot -= 2;
|
||||
|
||||
if (slot < nargs && !analysis->argEscapes(slot))
|
||||
return true;
|
||||
if (script->fun)
|
||||
slot -= script->fun->nargs;
|
||||
|
||||
return !analysis->localEscapes(slot);
|
||||
return analysis->liveness(slot).firstWrite(lifetime) == uint32(-1);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -394,7 +371,7 @@ LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe)
|
||||
* so need to update the register state at that entry point so that the right
|
||||
* things get loaded when we enter the loop.
|
||||
*/
|
||||
RegisterAllocation *entry = liveness->getCode(lifetime->entry).allocation;
|
||||
RegisterAllocation *entry = analysis->getAllocation(lifetime->entry);
|
||||
JS_ASSERT(entry && !entry->assigned(reg));
|
||||
entry->set(reg, slot, true);
|
||||
}
|
||||
@ -423,16 +400,12 @@ SafeSub(int32 one, int32 two, int32 *res)
|
||||
}
|
||||
|
||||
bool
|
||||
LoopState::hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped)
|
||||
LoopState::hoistArrayLengthCheck(const FrameEntry *obj, types::TypeSet *objTypes,
|
||||
unsigned indexPopped)
|
||||
{
|
||||
if (skipAnalysis || script->failedBoundsCheck)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Note: this should only be used when the object is known to be a dense
|
||||
* array (if it is an object at all).
|
||||
*/
|
||||
|
||||
obj = obj->backing();
|
||||
|
||||
JaegerSpew(JSpew_Analysis, "Trying to hoist bounds check on %s\n",
|
||||
@ -449,7 +422,6 @@ LoopState::hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped)
|
||||
* but it actually can, we will probably recompile after the hoisted
|
||||
* bounds check fails.
|
||||
*/
|
||||
types::TypeSet *objTypes = cc.getTypeSet(obj);
|
||||
JS_ASSERT(objTypes && !objTypes->unknown());
|
||||
if (!growArrays.empty()) {
|
||||
unsigned count = objTypes->getObjectCount();
|
||||
@ -492,7 +464,7 @@ LoopState::hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped)
|
||||
* underflow the array. We currently only hoist bounds checks for loops
|
||||
* which walk arrays going forward.
|
||||
*/
|
||||
if (!liveness->nonDecreasing(index, lifetime)) {
|
||||
if (!analysis->liveness(index).nonDecreasing(script, lifetime)) {
|
||||
JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
|
||||
return false;
|
||||
}
|
||||
@ -509,21 +481,17 @@ LoopState::hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped)
|
||||
if (index == testLHS && testLessEqual) {
|
||||
uint32 rhs = testRHS;
|
||||
|
||||
if (rhs != UNASSIGNED) {
|
||||
types::TypeSet *types = cc.getTypeSet(rhs);
|
||||
if (!types) {
|
||||
JaegerSpew(JSpew_Analysis, "Unknown type of branch test\n");
|
||||
return false;
|
||||
}
|
||||
if (testLength) {
|
||||
FrameEntry *rhsFE = frame.getOrTrack(rhs);
|
||||
FrameEntry *lengthEntry = invariantLength(rhsFE);
|
||||
if (!lengthEntry) {
|
||||
JaegerSpew(JSpew_Analysis, "Could not get invariant entry for length\n");
|
||||
return false;
|
||||
}
|
||||
rhs = frame.indexOfFe(lengthEntry);
|
||||
}
|
||||
if (testLength) {
|
||||
FrameEntry *rhsFE = frame.getOrTrack(rhs);
|
||||
FrameEntry *lengthEntry = invariantLength(rhsFE, NULL);
|
||||
|
||||
/*
|
||||
* An entry for the length should have been constructed while
|
||||
* processing the test.
|
||||
*/
|
||||
JS_ASSERT(lengthEntry);
|
||||
|
||||
rhs = frame.indexOfFe(lengthEntry);
|
||||
}
|
||||
|
||||
int32 constant;
|
||||
@ -612,7 +580,7 @@ LoopState::invariantSlots(const FrameEntry *obj)
|
||||
}
|
||||
|
||||
FrameEntry *
|
||||
LoopState::invariantLength(const FrameEntry *obj)
|
||||
LoopState::invariantLength(const FrameEntry *obj, types::TypeSet *objTypes)
|
||||
{
|
||||
if (skipAnalysis || script->failedBoundsCheck)
|
||||
return NULL;
|
||||
@ -630,12 +598,13 @@ LoopState::invariantLength(const FrameEntry *obj)
|
||||
}
|
||||
}
|
||||
|
||||
if (!objTypes)
|
||||
return NULL;
|
||||
|
||||
if (!loopInvariantEntry(frame.indexOfFe(obj)))
|
||||
return NULL;
|
||||
|
||||
/* Make sure this is a dense array whose length fits in an int32. */
|
||||
types::TypeSet *types = cc.getTypeSet(slot);
|
||||
types::ObjectKind kind = types ? types->getKnownObjectKind(cx) : types::OBJECT_UNKNOWN;
|
||||
types::ObjectKind kind = objTypes->getKnownObjectKind(cx);
|
||||
if (kind != types::OBJECT_DENSE_ARRAY && kind != types::OBJECT_PACKED_ARRAY)
|
||||
return NULL;
|
||||
|
||||
@ -644,14 +613,14 @@ LoopState::invariantLength(const FrameEntry *obj)
|
||||
* to the elements of any of the accessed arrays. This could invoke an
|
||||
* inline path which updates the length.
|
||||
*/
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
types::TypeObject *object = types->getObject(i);
|
||||
for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
|
||||
types::TypeObject *object = objTypes->getObject(i);
|
||||
if (!object)
|
||||
continue;
|
||||
if (object->unknownProperties() || hasModifiedProperty(object, JSID_VOID))
|
||||
return NULL;
|
||||
}
|
||||
types->addFreeze(cx);
|
||||
objTypes->addFreeze(cx);
|
||||
|
||||
uint32 which = frame.allocTemporary();
|
||||
if (which == uint32(-1))
|
||||
@ -676,9 +645,9 @@ LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm, Vector<Jump> *jump
|
||||
{
|
||||
/*
|
||||
* Restore all invariants in memory when entering the loop or after any
|
||||
* scripted or C++ call, and check that all hoisted conditions. Care should
|
||||
* be taken not to clobber the return register or callee-saved registers,
|
||||
* which may still be live after some calls.
|
||||
* scripted or C++ call, and check that all hoisted conditions still hold.
|
||||
* Care should be taken not to clobber the return register or callee-saved
|
||||
* registers, which may still be live after some calls.
|
||||
*/
|
||||
|
||||
Registers regs(Registers::TempRegs);
|
||||
@ -739,12 +708,9 @@ LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm, Vector<Jump> *jump
|
||||
|
||||
case InvariantEntry::INVARIANT_SLOTS:
|
||||
case InvariantEntry::INVARIANT_LENGTH: {
|
||||
/* Make sure this is an object before trying to access its slots or length. */
|
||||
uint32 array = entry.u.array.arraySlot;
|
||||
if (cc.getTypeSet(array)->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT) {
|
||||
Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
|
||||
jumps->append(notObject);
|
||||
}
|
||||
Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
|
||||
jumps->append(notObject);
|
||||
masm.loadPayload(frame.addressOf(array), T0);
|
||||
|
||||
uint32 offset = (entry.kind == InvariantEntry::INVARIANT_SLOTS)
|
||||
@ -764,17 +730,6 @@ LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm, Vector<Jump> *jump
|
||||
|
||||
/* Loop analysis methods. */
|
||||
|
||||
/* :XXX: factor out into more common code. */
|
||||
static inline uint32 localSlot(JSScript *script, uint32 local) {
|
||||
return 2 + (script->fun ? script->fun->nargs : 0) + local;
|
||||
}
|
||||
static inline uint32 argSlot(uint32 arg) {
|
||||
return 2 + arg;
|
||||
}
|
||||
static inline uint32 thisSlot() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Whether pc is a loop test operand accessing a variable modified by the loop. */
|
||||
bool
|
||||
LoopState::loopVariableAccess(jsbytecode *pc)
|
||||
@ -785,51 +740,21 @@ LoopState::loopVariableAccess(jsbytecode *pc)
|
||||
case JSOP_DECLOCAL:
|
||||
case JSOP_LOCALINC:
|
||||
case JSOP_LOCALDEC:
|
||||
if (analysis->localEscapes(GET_SLOTNO(pc)))
|
||||
return false;
|
||||
return liveness->firstWrite(localSlot(script, GET_SLOTNO(pc)), lifetime) != uint32(-1);
|
||||
case JSOP_GETARG:
|
||||
case JSOP_INCARG:
|
||||
case JSOP_DECARG:
|
||||
case JSOP_ARGINC:
|
||||
case JSOP_ARGDEC:
|
||||
if (analysis->argEscapes(GET_SLOTNO(pc)))
|
||||
case JSOP_ARGDEC: {
|
||||
uint32 slot = GetBytecodeSlot(script, pc);
|
||||
if (analysis->slotEscapes(slot))
|
||||
return false;
|
||||
return liveness->firstWrite(argSlot(GET_SLOTNO(pc)), lifetime) != uint32(-1);
|
||||
return analysis->liveness(slot).firstWrite(lifetime) != uint32(-1);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int32
|
||||
GetBytecodeInteger(jsbytecode *pc)
|
||||
{
|
||||
switch (JSOp(*pc)) {
|
||||
|
||||
case JSOP_ZERO:
|
||||
return 0;
|
||||
|
||||
case JSOP_ONE:
|
||||
return 1;
|
||||
|
||||
case JSOP_UINT16:
|
||||
return (int32_t) GET_UINT16(pc);
|
||||
|
||||
case JSOP_UINT24:
|
||||
return (int32_t) GET_UINT24(pc);
|
||||
|
||||
case JSOP_INT8:
|
||||
return GET_INT8(pc);
|
||||
|
||||
case JSOP_INT32:
|
||||
return GET_INT32(pc);
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Bad op");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get any slot/constant accessed by a loop test operand, in terms of its value
|
||||
* at the start of the next loop iteration.
|
||||
@ -850,37 +775,36 @@ LoopState::getLoopTestAccess(jsbytecode *pc, uint32 *pslot, int32 *pconstant)
|
||||
*/
|
||||
|
||||
JSOp op = JSOp(*pc);
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
|
||||
switch (op) {
|
||||
|
||||
case JSOP_GETLOCAL:
|
||||
case JSOP_INCLOCAL:
|
||||
case JSOP_DECLOCAL:
|
||||
case JSOP_LOCALINC:
|
||||
case JSOP_LOCALDEC: {
|
||||
uint32 local = GET_SLOTNO(pc);
|
||||
if (analysis->localEscapes(local))
|
||||
return false;
|
||||
*pslot = localSlot(script, local);
|
||||
if (op == JSOP_LOCALINC)
|
||||
*pconstant = -1;
|
||||
else if (op == JSOP_LOCALDEC)
|
||||
*pconstant = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
case JSOP_LOCALDEC:
|
||||
case JSOP_GETARG:
|
||||
case JSOP_INCARG:
|
||||
case JSOP_DECARG:
|
||||
case JSOP_ARGINC:
|
||||
case JSOP_ARGDEC: {
|
||||
uint32 arg = GET_SLOTNO(pc);
|
||||
if (analysis->argEscapes(arg))
|
||||
uint32 slot = GetBytecodeSlot(script, pc);
|
||||
if (analysis->slotEscapes(slot))
|
||||
return false;
|
||||
*pslot = argSlot(arg);
|
||||
if (op == JSOP_ARGINC)
|
||||
*pconstant = -1;
|
||||
else if (op == JSOP_ARGDEC)
|
||||
*pconstant = 1;
|
||||
|
||||
/* Only consider tests on known integers. */
|
||||
types::TypeSet *types = analysis->pushedTypes(pc, 0);
|
||||
if (types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
return false;
|
||||
|
||||
*pslot = slot;
|
||||
if (cs->format & JOF_POST) {
|
||||
if (cs->format & JOF_INC)
|
||||
*pconstant = -1;
|
||||
else
|
||||
*pconstant = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -913,10 +837,10 @@ LoopState::analyzeLoopTest()
|
||||
jsbytecode *backedge = script->code + lifetime->backedge;
|
||||
if (JSOp(*backedge) != JSOP_IFNE)
|
||||
return;
|
||||
StackAnalysis::PoppedValue test = stack.popped(backedge, 0);
|
||||
if (test.offset == StackAnalysis::UNKNOWN_PUSHED)
|
||||
const SSAValue &test = analysis->poppedValue(backedge, 0);
|
||||
if (test.kind() != SSAValue::PUSHED)
|
||||
return;
|
||||
JSOp cmpop = JSOp(script->code[test.offset]);
|
||||
JSOp cmpop = JSOp(script->code[test.pushedOffset()]);
|
||||
switch (cmpop) {
|
||||
case JSOP_GT:
|
||||
case JSOP_GE:
|
||||
@ -927,16 +851,14 @@ LoopState::analyzeLoopTest()
|
||||
return;
|
||||
}
|
||||
|
||||
StackAnalysis::PoppedValue poppedOne = stack.popped(test.offset, 1);
|
||||
StackAnalysis::PoppedValue poppedTwo = stack.popped(test.offset, 0);
|
||||
const SSAValue &poppedOne = analysis->poppedValue(test.pushedOffset(), 1);
|
||||
const SSAValue &poppedTwo = analysis->poppedValue(test.pushedOffset(), 0);
|
||||
|
||||
if (poppedOne.offset == StackAnalysis::UNKNOWN_PUSHED ||
|
||||
poppedTwo.offset == StackAnalysis::UNKNOWN_PUSHED) {
|
||||
if (poppedOne.kind() != SSAValue::PUSHED || poppedTwo.kind() != SSAValue::PUSHED)
|
||||
return;
|
||||
}
|
||||
|
||||
jsbytecode *one = script->code + poppedOne.offset;
|
||||
jsbytecode *two = script->code + poppedTwo.offset;
|
||||
jsbytecode *one = script->code + poppedOne.pushedOffset();
|
||||
jsbytecode *two = script->code + poppedTwo.pushedOffset();
|
||||
|
||||
/* Reverse the condition if the RHS is modified by the loop. */
|
||||
if (loopVariableAccess(two)) {
|
||||
@ -961,33 +883,24 @@ LoopState::analyzeLoopTest()
|
||||
|
||||
if (JSOp(*two) == JSOP_LENGTH) {
|
||||
/* Handle 'this.length' or 'x.length' for loop invariant 'x'. */
|
||||
StackAnalysis::PoppedValue array = stack.popped(two, 0);
|
||||
if (array.offset == StackAnalysis::UNKNOWN_PUSHED)
|
||||
const SSAValue &array = analysis->poppedValue(two, 0);
|
||||
if (array.kind() != SSAValue::PUSHED)
|
||||
return;
|
||||
jsbytecode *arraypc = script->code + array.offset;
|
||||
jsbytecode *arraypc = script->code + array.pushedOffset();
|
||||
if (loopVariableAccess(arraypc))
|
||||
return;
|
||||
switch (JSOp(*arraypc)) {
|
||||
case JSOP_GETLOCAL: {
|
||||
uint32 local = GET_SLOTNO(arraypc);
|
||||
if (analysis->localEscapes(local))
|
||||
return;
|
||||
rhs = localSlot(script, local);
|
||||
case JSOP_GETLOCAL:
|
||||
case JSOP_GETARG:
|
||||
case JSOP_THIS: {
|
||||
rhs = GetBytecodeSlot(script, arraypc);
|
||||
break;
|
||||
}
|
||||
case JSOP_GETARG: {
|
||||
uint32 arg = GET_SLOTNO(arraypc);
|
||||
if (analysis->argEscapes(arg))
|
||||
return;
|
||||
rhs = argSlot(arg);
|
||||
break;
|
||||
}
|
||||
case JSOP_THIS:
|
||||
rhs = thisSlot();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (!invariantLength(frame.getOrTrack(rhs), analysis->getValueTypes(array)))
|
||||
return;
|
||||
rhsLength = true;
|
||||
} else {
|
||||
if (!getLoopTestAccess(two, &rhs, &rhsConstant))
|
||||
@ -997,16 +910,6 @@ LoopState::analyzeLoopTest()
|
||||
if (lhs == UNASSIGNED)
|
||||
return;
|
||||
|
||||
/* Only consider comparisons on known integers. */
|
||||
types::TypeSet *lhsTypes = cc.getTypeSet(lhs);
|
||||
if (!lhsTypes || lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
return;
|
||||
if (rhs != UNASSIGNED && !rhsLength) {
|
||||
types::TypeSet *rhsTypes = cc.getTypeSet(rhs);
|
||||
if (!rhsTypes || rhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
return;
|
||||
}
|
||||
|
||||
int32 constant;
|
||||
if (!SafeSub(rhsConstant, lhsConstant, &constant))
|
||||
return;
|
||||
@ -1037,49 +940,26 @@ LoopState::analyzeLoopIncrements()
|
||||
* also handle the first basic block).
|
||||
*/
|
||||
|
||||
unsigned nargs = script->fun ? script->fun->nargs : 0;
|
||||
for (unsigned i = 0; i < nargs; i++) {
|
||||
if (analysis->argEscapes(i))
|
||||
for (uint32 slot = ArgSlot(0); slot < LocalSlot(script, script->nfixed); slot++) {
|
||||
if (analysis->slotEscapes(slot))
|
||||
continue;
|
||||
|
||||
uint32 offset = liveness->onlyWrite(argSlot(i), lifetime);
|
||||
uint32 offset = analysis->liveness(slot).onlyWrite(lifetime);
|
||||
if (offset == uint32(-1) || offset < lifetime->lastBlock)
|
||||
continue;
|
||||
|
||||
JSOp op = JSOp(script->code[offset]);
|
||||
if (op == JSOP_SETARG)
|
||||
continue;
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
if (cs->format & (JOF_INC | JOF_DEC)) {
|
||||
types::TypeSet *types = analysis->pushedTypes(offset);
|
||||
if (types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
continue;
|
||||
|
||||
types::TypeSet *types = cc.getTypeSet(argSlot(i));
|
||||
if (!types || types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
continue;
|
||||
|
||||
Increment inc;
|
||||
inc.slot = argSlot(i);
|
||||
inc.offset = offset;
|
||||
increments.append(inc);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < script->nfixed; i++) {
|
||||
if (analysis->localEscapes(i))
|
||||
continue;
|
||||
|
||||
uint32 offset = liveness->onlyWrite(localSlot(script, i), lifetime);
|
||||
if (offset == uint32(-1) || offset < lifetime->lastBlock)
|
||||
continue;
|
||||
|
||||
JSOp op = JSOp(script->code[offset]);
|
||||
if (op == JSOP_SETLOCAL || op == JSOP_SETLOCALPOP)
|
||||
continue;
|
||||
|
||||
types::TypeSet *types = cc.getTypeSet(localSlot(script, i));
|
||||
if (!types || types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
|
||||
continue;
|
||||
|
||||
Increment inc;
|
||||
inc.slot = localSlot(script, i);
|
||||
inc.offset = offset;
|
||||
increments.append(inc);
|
||||
Increment inc;
|
||||
inc.slot = slot;
|
||||
inc.offset = offset;
|
||||
increments.append(inc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1104,8 +984,8 @@ LoopState::analyzeModset()
|
||||
|
||||
case JSOP_SETHOLE:
|
||||
case JSOP_SETELEM: {
|
||||
types::TypeSet *objTypes = poppedTypes(pc, 2);
|
||||
types::TypeSet *elemTypes = poppedTypes(pc, 1);
|
||||
types::TypeSet *objTypes = analysis->poppedTypes(pc, 2);
|
||||
types::TypeSet *elemTypes = analysis->poppedTypes(pc, 1);
|
||||
|
||||
/*
|
||||
* Mark the modset as unknown if the index might be non-integer,
|
||||
@ -1251,15 +1131,6 @@ LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32 slot)
|
||||
}
|
||||
}
|
||||
|
||||
inline types::TypeSet *
|
||||
LoopState::poppedTypes(jsbytecode *pc, unsigned which)
|
||||
{
|
||||
StackAnalysis::PoppedValue value = stack.popped(pc, which);
|
||||
if (value.offset == StackAnalysis::UNKNOWN_PUSHED)
|
||||
return NULL;
|
||||
return script->types->pushed(value.offset, value.which);
|
||||
}
|
||||
|
||||
bool
|
||||
LoopState::getEntryValue(uint32 offset, uint32 popped, uint32 *pslot, int32 *pconstant)
|
||||
{
|
||||
@ -1268,42 +1139,31 @@ LoopState::getEntryValue(uint32 offset, uint32 popped, uint32 *pslot, int32 *pco
|
||||
* expression 'slot + constant' with the same value as the stack value
|
||||
* and expressed in terms of the state at loop entry.
|
||||
*/
|
||||
StackAnalysis::PoppedValue value = stack.popped(offset, popped);
|
||||
if (value.offset == StackAnalysis::UNKNOWN_PUSHED)
|
||||
const SSAValue &value = analysis->poppedValue(offset, popped);
|
||||
if (value.kind() != SSAValue::PUSHED)
|
||||
return false;
|
||||
|
||||
jsbytecode *pc = script->code + value.offset;
|
||||
jsbytecode *pc = script->code + value.pushedOffset();
|
||||
JSOp op = (JSOp)*pc;
|
||||
|
||||
switch (op) {
|
||||
|
||||
case JSOP_GETLOCAL:
|
||||
case JSOP_LOCALINC:
|
||||
case JSOP_INCLOCAL: {
|
||||
uint32 local = GET_SLOTNO(pc);
|
||||
if (analysis->localEscapes(local))
|
||||
return false;
|
||||
uint32 write = liveness->firstWrite(localSlot(script, local), lifetime);
|
||||
if (write != uint32(-1) && write < value.offset) {
|
||||
/* Variable has been modified since the start of the loop. */
|
||||
return false;
|
||||
}
|
||||
*pslot = localSlot(script, local);
|
||||
*pconstant = (op == JSOP_INCLOCAL) ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
case JSOP_INCLOCAL:
|
||||
case JSOP_GETARG:
|
||||
case JSOP_ARGINC:
|
||||
case JSOP_INCARG: {
|
||||
uint32 arg = GET_SLOTNO(pc);
|
||||
if (analysis->argEscapes(arg))
|
||||
uint32 slot = GetBytecodeSlot(script, pc);
|
||||
if (analysis->slotEscapes(slot))
|
||||
return false;
|
||||
uint32 write = liveness->firstWrite(argSlot(arg), lifetime);
|
||||
if (write != uint32(-1) && write < value.offset)
|
||||
uint32 write = analysis->liveness(slot).firstWrite(lifetime);
|
||||
if (write != uint32(-1) && write < value.pushedOffset()) {
|
||||
/* Variable has been modified since the start of the loop. */
|
||||
return false;
|
||||
*pslot = argSlot(arg);
|
||||
*pconstant = (op == JSOP_INCARG) ? 1 : 0;
|
||||
}
|
||||
*pslot = slot;
|
||||
*pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -89,13 +89,12 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
{
|
||||
JSContext *cx;
|
||||
JSScript *script;
|
||||
analyze::ScriptAnalysis *analysis;
|
||||
Compiler &cc;
|
||||
FrameState &frame;
|
||||
analyze::Script *analysis;
|
||||
analyze::LifetimeScript *liveness;
|
||||
|
||||
/* Basic information about this loop. */
|
||||
analyze::LifetimeLoop *lifetime;
|
||||
analyze::LoopAnalysis *lifetime;
|
||||
|
||||
/* Allocation at the head of the loop, has all loop carried variables. */
|
||||
RegisterAllocation *alloc;
|
||||
@ -179,7 +178,8 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
Vector<InvariantEntry, 4, CompilerAllocPolicy> invariantEntries;
|
||||
|
||||
bool loopInvariantEntry(uint32 slot);
|
||||
bool addHoistedCheck(uint32 arraySlot, uint32 valueSlot1, uint32 valueSlot2, int32 constant);
|
||||
bool addHoistedCheck(uint32 arraySlot,
|
||||
uint32 valueSlot1, uint32 valueSlot2, int32 constant);
|
||||
void addNegativeCheck(uint32 valueSlot, int32 constant);
|
||||
|
||||
bool hasInvariants() { return !invariantEntries.empty(); }
|
||||
@ -194,8 +194,7 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
jsbytecode *PC;
|
||||
|
||||
LoopState(JSContext *cx, JSScript *script,
|
||||
Compiler *cc, FrameState *frame,
|
||||
analyze::Script *analysis, analyze::LifetimeScript *liveness);
|
||||
Compiler *cc, FrameState *frame);
|
||||
bool init(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
|
||||
|
||||
bool generatingInvariants() { return !skipAnalysis; }
|
||||
@ -234,16 +233,18 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
|
||||
void flushLoop(StubCompiler &stubcc);
|
||||
|
||||
bool hoistArrayLengthCheck(const FrameEntry *obj, unsigned indexPopped);
|
||||
/*
|
||||
* These should only be used for entries which are known to be dense arrays
|
||||
* (if they are objects at all).
|
||||
*/
|
||||
bool hoistArrayLengthCheck(const FrameEntry *obj, types::TypeSet *objTypes,
|
||||
unsigned indexPopped);
|
||||
FrameEntry *invariantSlots(const FrameEntry *obj);
|
||||
FrameEntry *invariantLength(const FrameEntry *obj);
|
||||
FrameEntry *invariantLength(const FrameEntry *obj, types::TypeSet *objTypes);
|
||||
|
||||
private:
|
||||
/* Analysis information for the loop. */
|
||||
|
||||
/* Stack information at points within this loop. */
|
||||
analyze::StackAnalysis stack;
|
||||
|
||||
/*
|
||||
* Any inequality known to hold at the head of the loop. This has the
|
||||
* form 'lhs <= rhs + constant' or 'lhs >= rhs + constant', depending on
|
||||
@ -262,6 +263,7 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
* length must not be directly modified within the loop.
|
||||
*/
|
||||
bool testLength;
|
||||
bool testLengthKnownObject;
|
||||
|
||||
/*
|
||||
* A variable which will be incremented or decremented exactly once in each
|
||||
@ -306,8 +308,6 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
uint32 getIncrement(uint32 slot);
|
||||
int32 adjustConstantForIncrement(jsbytecode *pc, uint32 slot);
|
||||
|
||||
inline types::TypeSet *poppedTypes(jsbytecode *pc, unsigned which);
|
||||
|
||||
bool getEntryValue(uint32 offset, uint32 popped, uint32 *pslot, int32 *pconstant);
|
||||
};
|
||||
|
||||
|
@ -744,35 +744,6 @@ js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
|
||||
JS_ASSERT(!TRACE_RECORDER(cx));
|
||||
#endif
|
||||
|
||||
JSScript *script = cx->fp()->script();
|
||||
if (cx->typeInferenceEnabled() && script->varTypes) {
|
||||
/*
|
||||
* Convert integer locals/args to doubles as required. The code we are
|
||||
* jumping to may assume that non-escaping locals and args have double
|
||||
* values if they were inferred as 'int or double'. The interpreter cannot
|
||||
* guarantee this holds, so we check and fixup the args/locals here.
|
||||
*/
|
||||
|
||||
if (cx->fp()->hasArgs()) {
|
||||
JSFunction *fun = cx->fp()->fun();
|
||||
Value *formals = cx->fp()->formalArgs();
|
||||
for (uint32 i = 0; i < fun->nargs; i++) {
|
||||
if (formals[i].isInt32() &&
|
||||
script->argTypes(i)->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
|
||||
formals[i].setDouble((double)formals[i].toInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value *fixed = cx->fp()->slots();
|
||||
for (uint32 i = 0; i < script->nfixed; i++) {
|
||||
if (fixed[i].isInt32() &&
|
||||
script->localTypes(i)->getKnownTypeTag(cx) == JSVAL_TYPE_DOUBLE) {
|
||||
fixed[i].setDouble((double)fixed[i].toInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
|
||||
}
|
||||
|
||||
|
@ -1546,7 +1546,7 @@ class ScopeNameCompiler : public PICStubCompiler
|
||||
JSScript *newscript = getprop.obj->getCallObjCalleeFunction()->script();
|
||||
uint16 slot = uint16(getprop.shape->shortid);
|
||||
if (!newscript->ensureVarTypes(cx))
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return false;
|
||||
if (shape->getterOp() == GetCallArg)
|
||||
types = newscript->argTypes(slot);
|
||||
else if (shape->getterOp() == GetCallVar)
|
||||
@ -1555,15 +1555,15 @@ class ScopeNameCompiler : public PICStubCompiler
|
||||
JS_ASSERT(!getprop.obj->getParent());
|
||||
if (getprop.obj->getType()->unknownProperties()) {
|
||||
f.script()->typeMonitorResult(cx, f.pc(), types::TYPE_UNKNOWN);
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
types = getprop.obj->getType()->getProperty(cx, shape->id, false);
|
||||
if (!types)
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
types->pushAllTypes(cx, f.script(), f.pc());
|
||||
return cx->compartment->types.checkPendingRecompiles(cx);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -559,10 +559,6 @@ Recompiler::recompile()
|
||||
JS_ASSERT_IF(keepCtor, script->jitCtor);
|
||||
|
||||
cx->compartment->types.recompilations++;
|
||||
|
||||
if (!cx->compartment->types.checkPendingRecompiles(cx))
|
||||
return Compile_Error;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2840,22 +2840,21 @@ stubs::CheckArgumentTypes(VMFrame &f)
|
||||
JSScript *script = fun->script();
|
||||
RecompilationMonitor monitor(f.cx);
|
||||
|
||||
/* Postpone recompilations until all args have been updated. */
|
||||
types::AutoEnterTypeInference enter(f.cx);
|
||||
{
|
||||
/* Postpone recompilations until all args have been updated. */
|
||||
types::AutoEnterTypeInference enter(f.cx);
|
||||
|
||||
if (!f.fp()->isConstructing()) {
|
||||
if (!script->typeSetThis(f.cx, fp->thisValue()))
|
||||
THROW();
|
||||
if (!f.fp()->isConstructing()) {
|
||||
if (!script->typeSetThis(f.cx, fp->thisValue()))
|
||||
THROW();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < fun->nargs; i++) {
|
||||
if (!script->typeSetArgument(f.cx, i, fp->formalArg(i)))
|
||||
THROW();
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < fun->nargs; i++) {
|
||||
if (!script->typeSetArgument(f.cx, i, fp->formalArg(i)))
|
||||
THROW();
|
||||
}
|
||||
|
||||
if (!f.cx->compartment->types.checkPendingRecompiles(f.cx))
|
||||
THROW();
|
||||
|
||||
if (monitor.recompiled())
|
||||
return;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user