[INFER] Bytecode SSA analysis, bug 650715.

This commit is contained in:
Brian Hackett 2011-04-22 07:59:45 -07:00
parent 361316ea19
commit 7c7bcb48fd
25 changed files with 2585 additions and 2203 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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___

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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_;

View File

@ -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))

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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;