mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
Analysis and recompilation tuning for SS, bug 608746.
This commit is contained in:
parent
63ce41978f
commit
d732bb9424
@ -469,7 +469,7 @@ Script::analyze(JSContext *cx)
|
||||
code->pushedArray[i].setInnerStack(stack);
|
||||
stack = &code->pushedArray[i];
|
||||
|
||||
types::InferSpew(types::ISpewOps, "pushed #%u:%05u %u T%u\n",
|
||||
types::InferSpew(types::ISpewOps, "pushed #%u:%05u %u T%u",
|
||||
id, offset, i, stack->types.id());
|
||||
}
|
||||
|
||||
@ -759,10 +759,13 @@ Script::analyze(JSContext *cx)
|
||||
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
/* Generate type constraints for the script. */
|
||||
|
||||
AnalyzeState state;
|
||||
state.init(cx, script);
|
||||
|
||||
offset = 0;
|
||||
TypeState state;
|
||||
while (offset < script->length) {
|
||||
analyze::Bytecode *code = maybeCode(offset);
|
||||
Bytecode *code = maybeCode(offset);
|
||||
|
||||
jsbytecode *pc = script->code + offset;
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
@ -772,6 +775,8 @@ Script::analyze(JSContext *cx)
|
||||
if (code && code->analyzed)
|
||||
analyzeTypes(cx, code, state);
|
||||
}
|
||||
|
||||
state.destroy(cx);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -139,12 +139,6 @@ struct Bytecode
|
||||
*/
|
||||
bool hasIncDecOverflow : 1;
|
||||
|
||||
/*
|
||||
* For logging, whether we've generated warnings due to a mismatch between the
|
||||
* actual and inferred types at this bytecode.
|
||||
*/
|
||||
bool missingTypes : 1;
|
||||
|
||||
/* Pool which constraints on this instruction should use. */
|
||||
inline JSArenaPool &pool();
|
||||
|
||||
@ -163,7 +157,9 @@ struct Bytecode
|
||||
*/
|
||||
inline types::TypeObject* getInitObject(JSContext *cx, bool isArray);
|
||||
|
||||
#ifdef DEBUG
|
||||
void print(JSContext *cx);
|
||||
#endif
|
||||
|
||||
#endif /* JS_TYPE_INFERENCE */
|
||||
|
||||
@ -349,37 +345,115 @@ class Script
|
||||
/* Bytecode where this script is nested. */
|
||||
inline Bytecode *parentCode();
|
||||
|
||||
void print(JSContext *cx);
|
||||
/* Gather statistics off this script and print it if necessary. */
|
||||
void finish(JSContext *cx);
|
||||
|
||||
/* Helpers */
|
||||
|
||||
/* Temporary state for handling opcodes with fused behavior. */
|
||||
struct TypeState {
|
||||
/* Inference state destroyed after the initial pass through the function. */
|
||||
|
||||
struct AnalyzeStateStack {
|
||||
/* Whether this node is the iterator for a 'for each' loop. */
|
||||
bool isForEach;
|
||||
|
||||
/* Variable set for any scope name binding pushed on this stack node. */
|
||||
types::VariableSet *scopeVars;
|
||||
|
||||
/* Any value pushed by a JSOP_DOUBLE. */
|
||||
bool hasDouble;
|
||||
double doubleValue;
|
||||
|
||||
/* Whether this is or could be the constant zero. */
|
||||
bool isZero;
|
||||
|
||||
/* Whether this is another constant. */
|
||||
bool isConstant;
|
||||
};
|
||||
|
||||
struct AnalyzeState {
|
||||
AnalyzeStateStack *stack;
|
||||
|
||||
/* Current stack depth. */
|
||||
unsigned stackDepth;
|
||||
|
||||
/* Last opcode was JSOP_GETTER or JSOP_SETTER. */
|
||||
bool hasGetSet;
|
||||
|
||||
/* Last opcode was JSOP_HOLE. */
|
||||
bool hasHole;
|
||||
|
||||
TypeState()
|
||||
: hasGetSet(false), hasHole(false)
|
||||
/* Locals thought to be zero/constants. */
|
||||
bool zeroLocals[4];
|
||||
uint32 constLocals[4];
|
||||
unsigned numConstLocals;
|
||||
|
||||
AnalyzeState()
|
||||
: stack(NULL), stackDepth(0), hasGetSet(false), hasHole(false), numConstLocals(0)
|
||||
{}
|
||||
|
||||
bool init(JSContext *cx, JSScript *script)
|
||||
{
|
||||
if (script->nslots) {
|
||||
stack = (AnalyzeStateStack *)
|
||||
cx->calloc(script->nslots * sizeof(AnalyzeStateStack));
|
||||
return (stack != NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy(JSContext *cx)
|
||||
{
|
||||
cx->free(stack);
|
||||
}
|
||||
|
||||
AnalyzeStateStack &popped(unsigned i) {
|
||||
JS_ASSERT(i < stackDepth);
|
||||
return stack[stackDepth - 1 - i];
|
||||
}
|
||||
|
||||
const AnalyzeStateStack &popped(unsigned i) const {
|
||||
JS_ASSERT(i < stackDepth);
|
||||
return stack[stackDepth - 1 - i];
|
||||
}
|
||||
|
||||
void addConstLocal(uint32 local, bool zero) {
|
||||
if (numConstLocals == JS_ARRAY_LENGTH(constLocals))
|
||||
return;
|
||||
if (maybeLocalConst(local, false))
|
||||
return;
|
||||
zeroLocals[numConstLocals] = zero;
|
||||
constLocals[numConstLocals++] = local;
|
||||
}
|
||||
|
||||
bool maybeLocalConst(uint32 local, bool zero) {
|
||||
for (unsigned i = 0; i < numConstLocals; i++) {
|
||||
if (constLocals[i] == local)
|
||||
return !zero || zeroLocals[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clearLocal(uint32 local) {
|
||||
for (unsigned i = 0; i < numConstLocals; i++) {
|
||||
if (constLocals[i] == local) {
|
||||
constLocals[i] = constLocals[--numConstLocals];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Analyzes a bytecode, generating type constraints describing its behavior. */
|
||||
void analyzeTypes(JSContext *cx, Bytecode *codeType, TypeState &state);
|
||||
void analyzeTypes(JSContext *cx, Bytecode *code, AnalyzeState &state);
|
||||
|
||||
/*
|
||||
* Get the name to use for the local with specified index. Stack indicates the
|
||||
* point of the access, for looking up let variables.
|
||||
*/
|
||||
inline jsid getLocalId(unsigned index, types::TypeStack *stack);
|
||||
/* Get the name to use for the local with specified index. */
|
||||
inline jsid getLocalId(unsigned index, Bytecode *code);
|
||||
|
||||
/* Get the name to use for the argument with the specified index. */
|
||||
inline jsid getArgumentId(unsigned index);
|
||||
|
||||
/* Get the type set to use for a stack slot at a fixed stack depth. */
|
||||
inline types::TypeSet *getStackTypes(unsigned index, types::TypeStack *stack);
|
||||
inline types::TypeSet *getStackTypes(unsigned index, Bytecode *code);
|
||||
|
||||
/* Get any known type tag for an argument or local variable. */
|
||||
inline JSValueType knownArgumentTypeTag(JSContext *cx, JSScript *script, unsigned arg);
|
||||
|
@ -4303,9 +4303,8 @@ JS_TypeHandlerMissing(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssi
|
||||
TypeCallsite *site = Valueify(jssite);
|
||||
|
||||
/* Don't mark the return type as anything, and add a warning. */
|
||||
cx->compartment->types.warnings = true;
|
||||
InferSpew(ISpewDynamic, "warning: Call to unimplemented handler at #%u:%05u: %s\n",
|
||||
site->code->script->id, site->code->offset, TypeIdString(cx, fun->name));
|
||||
TypeFailure(cx, "Call to unimplemented handler at #%u:%05u: %s",
|
||||
site->code->script->id, site->code->offset, TypeIdString(fun->name));
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -4876,7 +4875,7 @@ JS_MakeTypeObject(JSContext *cx, const char *name, JSBool monitorNeeded, JSBool
|
||||
proto->addPropagate(cx, type);
|
||||
|
||||
if (monitorNeeded)
|
||||
cx->monitorTypeObject(type);
|
||||
cx->markTypeObjectUnknownProperties(type);
|
||||
|
||||
return (JSTypeObject*) type;
|
||||
#endif
|
||||
|
@ -2135,7 +2135,7 @@ array_push(JSContext *cx, uintN argc, Value *vp)
|
||||
return JS_FALSE;
|
||||
|
||||
if (cx->isTypeCallerMonitored())
|
||||
cx->monitorTypeObject(obj->getTypeObject());
|
||||
cx->markTypeObjectUnknownProperties(obj->getTypeObject());
|
||||
|
||||
if (argc != 1 || !obj->isDenseArray())
|
||||
return array_push_slowly(cx, obj, argc, vp + 2, vp);
|
||||
@ -2263,7 +2263,7 @@ array_unshift(JSContext *cx, uintN argc, Value *vp)
|
||||
return JS_FALSE;
|
||||
|
||||
if (cx->isTypeCallerMonitored())
|
||||
cx->monitorTypeObject(obj->getTypeObject());
|
||||
cx->markTypeObjectUnknownProperties(obj->getTypeObject());
|
||||
|
||||
newlen = length;
|
||||
if (argc > 0) {
|
||||
@ -2325,13 +2325,13 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
* result of the call so mark it at the callsite.
|
||||
*/
|
||||
objType = cx->getTypeCallerInitObject(true);
|
||||
cx->monitorTypeObject(objType);
|
||||
cx->markTypeObjectUnknownProperties(objType);
|
||||
cx->markTypeCallerUnexpected((jstype) objType);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cx->isTypeCallerMonitored())
|
||||
cx->monitorTypeObject(objType);
|
||||
cx->markTypeObjectUnknownProperties(objType);
|
||||
|
||||
/*
|
||||
* Create a new array value to return. Our ECMA v2 proposal specs
|
||||
@ -2487,7 +2487,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
TypeObject *ntype = cx->getTypeCallerInitObject(true);
|
||||
|
||||
if (cx->isTypeCallerMonitored())
|
||||
cx->monitorTypeObject(ntype);
|
||||
cx->markTypeObjectUnknownProperties(ntype);
|
||||
|
||||
/* Create a new Array object and root it using *vp. */
|
||||
JSObject *aobj = ComputeThisFromVp(cx, vp);
|
||||
@ -2623,13 +2623,13 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
||||
* result of the call so mark it at the callsite.
|
||||
*/
|
||||
objType = cx->getTypeCallerInitObject(true);
|
||||
cx->monitorTypeObject(objType);
|
||||
cx->markTypeObjectUnknownProperties(objType);
|
||||
cx->markTypeCallerUnexpected((jstype) objType);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cx->isTypeCallerMonitored())
|
||||
cx->monitorTypeObject(objType);
|
||||
cx->markTypeObjectUnknownProperties(objType);
|
||||
|
||||
if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
@ -2848,7 +2848,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
||||
* the output array, monitor any reads on the array in the future.
|
||||
*/
|
||||
if (cx->isTypeCallerMonitored() && (mode == MAP || mode == FILTER))
|
||||
cx->monitorTypeObject(newtype);
|
||||
cx->markTypeObjectUnknownProperties(newtype);
|
||||
|
||||
/*
|
||||
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
||||
@ -3307,7 +3307,7 @@ js_Array(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
if (cx->isTypeCallerMonitored() && vector)
|
||||
cx->monitorTypeObject(type);
|
||||
cx->markTypeObjectUnknownProperties(type);
|
||||
|
||||
/* Whether called with 'new' or not, use a new Array object. */
|
||||
JSObject *obj = NewDenseArrayObject(cx, type, length);
|
||||
@ -3381,6 +3381,7 @@ static void array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *
|
||||
if (site->argumentCount > 1) {
|
||||
for (size_t ind = 0; ind < site->argumentCount; ind++)
|
||||
site->argumentTypes[ind]->addSubset(cx, site->pool(), indexTypes);
|
||||
object->possiblePackedArray = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -2455,13 +2455,10 @@ public:
|
||||
inline void aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid second);
|
||||
|
||||
/* Mark an array type as being not packed and, possibly, not dense. */
|
||||
inline void markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense);
|
||||
inline void markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense, bool dynamic = true);
|
||||
|
||||
/*
|
||||
* Monitor future reads from the a type object. Instances may have properties
|
||||
* the inference does not know about.
|
||||
*/
|
||||
inline void monitorTypeObject(js::types::TypeObject *obj);
|
||||
/* Monitor all properties of a type object as unknown. */
|
||||
inline void markTypeObjectUnknownProperties(js::types::TypeObject *obj);
|
||||
|
||||
private:
|
||||
/* To silence MSVC warning about using 'this' in a member initializer. */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -290,38 +290,30 @@ struct TypeStack
|
||||
*/
|
||||
TypeStack *mergedGroup;
|
||||
|
||||
/* Identifier for this class within the script. filled in during printing. */
|
||||
int id;
|
||||
|
||||
/* Number of nodes beneath this one in the stack. */
|
||||
unsigned stackDepth;
|
||||
|
||||
/* Equivalence class for the node beneath this one in the stack. */
|
||||
TypeStack *innerStack;
|
||||
|
||||
/* Possible types for values at this stack node. */
|
||||
TypeSet types;
|
||||
|
||||
/* Whether any other stack nodes have been merged into this one. */
|
||||
bool hasMerged;
|
||||
|
||||
/* Whether the values at this node are bound by a 'with'. */
|
||||
/*
|
||||
* Any let variable associated with this stack node, and whether the values
|
||||
* at this node are bound by a 'with'. For resolving ambiguous cross-script
|
||||
* local variable lookups. :TODO: remove.
|
||||
*/
|
||||
jsid letVariable;
|
||||
bool boundWith;
|
||||
|
||||
/* Whether this node is the iterator for a 'for each' or 'for in' loop. */
|
||||
bool isForEach;
|
||||
|
||||
/*
|
||||
* Whether to ignore the type tag of this stack entry downstream; it may not
|
||||
* represent the actual values in this slot.
|
||||
*/
|
||||
bool ignoreTypeTag;
|
||||
|
||||
/* The name of any 'let' variable stored by this node. */
|
||||
jsid letVariable;
|
||||
|
||||
/* Variable set for any scope name binding pushed on this stack node. */
|
||||
VariableSet *scopeVars;
|
||||
#ifdef DEBUG
|
||||
/* Identifier for this class within the script. filled in during printing. */
|
||||
int id;
|
||||
#endif
|
||||
|
||||
/* Get the representative node for the equivalence class of this node. */
|
||||
inline TypeStack* group();
|
||||
@ -419,8 +411,11 @@ struct VariableSet
|
||||
|
||||
JSArenaPool *pool;
|
||||
|
||||
/* Whether the variables in this set are unknown. */
|
||||
bool unknown;
|
||||
|
||||
VariableSet(JSArenaPool *pool)
|
||||
: variables(NULL), propagateSet(NULL), propagateCount(NULL), pool(pool)
|
||||
: variables(NULL), propagateSet(NULL), propagateCount(NULL), pool(pool), unknown(false)
|
||||
{
|
||||
JS_ASSERT(pool);
|
||||
}
|
||||
@ -436,6 +431,9 @@ struct VariableSet
|
||||
*/
|
||||
bool addPropagate(JSContext *cx, VariableSet *target, bool excludePrototype);
|
||||
|
||||
/* Mark all existing and future properties of this set as unknown. */
|
||||
void markUnknown(JSContext *cx);
|
||||
|
||||
void print(JSContext *cx);
|
||||
};
|
||||
|
||||
@ -452,12 +450,6 @@ struct TypeObject
|
||||
/* Whether this is a function object, and may be cast into TypeFunction. */
|
||||
bool isFunction;
|
||||
|
||||
/*
|
||||
* Whether all reads from this object need to be monitored. This includes
|
||||
* all property and element accesses, and for functions all calls to the function.
|
||||
*/
|
||||
bool monitored;
|
||||
|
||||
/*
|
||||
* Properties of this object. This is filled in lazily for function objects
|
||||
* to avoid unnecessary property and prototype object creation. Don't access
|
||||
@ -489,6 +481,13 @@ struct TypeObject
|
||||
/* Whether all objects this represents are packed arrays (implies isDenseArray). */
|
||||
bool isPackedArray;
|
||||
|
||||
/*
|
||||
* Whether this object is thought to be a possible packed array: either it came
|
||||
* from a [a,b,c] initializer, an Array(a,b,c) call, or is another array for
|
||||
* which we've seen what looks like initialization code. This is pure heuristic.
|
||||
*/
|
||||
bool possiblePackedArray;
|
||||
|
||||
/* Make an object with the specified name. */
|
||||
TypeObject(JSContext *cx, JSArenaPool *pool, jsid id, bool isArray);
|
||||
|
||||
@ -507,6 +506,9 @@ struct TypeObject
|
||||
/* Get the properties of this object, filled in lazily. */
|
||||
inline VariableSet& properties(JSContext *cx);
|
||||
|
||||
/* Whether the properties of this object are unknown. */
|
||||
bool unknownProperties() { return propertySet.unknown; }
|
||||
|
||||
/* Get the type set for all integer index properties of this object. */
|
||||
inline TypeSet* indexTypes(JSContext *cx);
|
||||
|
||||
@ -745,18 +747,6 @@ struct TypeCompartment
|
||||
|
||||
/* Logging fields */
|
||||
|
||||
/*
|
||||
* Whether any warnings were emitted. These are nonfatal but (generally)
|
||||
* indicate unhandled constructs leading to analysis unsoundness.
|
||||
*/
|
||||
bool warnings;
|
||||
|
||||
/*
|
||||
* Whether to ignore generated warnings. For handling regressions with
|
||||
* shell functions we don't model.
|
||||
*/
|
||||
bool ignoreWarnings;
|
||||
|
||||
/*
|
||||
* The total time (in microseconds) spent generating inference structures
|
||||
* and performing analysis.
|
||||
@ -768,6 +758,9 @@ struct TypeCompartment
|
||||
unsigned typeCounts[TYPE_COUNT_LIMIT];
|
||||
unsigned typeCountOver;
|
||||
|
||||
/* Number of recompilations triggered. */
|
||||
unsigned recompilations;
|
||||
|
||||
void init();
|
||||
~TypeCompartment();
|
||||
|
||||
@ -826,17 +819,19 @@ enum SpewChannel {
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/* Spew with INFERFLAGS = full or base */
|
||||
void InferSpew(SpewChannel which, const char *fmt, ...);
|
||||
void InferSpewType(SpewChannel which, JSContext *cx, jstype type, const char *fmt, ...);
|
||||
const char * TypeString(jstype type);
|
||||
|
||||
#else
|
||||
|
||||
inline void InferSpew(SpewChannel which, const char *fmt, ...) {}
|
||||
inline void InferSpewType(SpewChannel which, JSContext *cx, jstype type, const char *fmt, ...) {}
|
||||
inline const char * TypeString(jstype type) { return NULL; }
|
||||
|
||||
#endif
|
||||
|
||||
/* Print a warning, dump state and abort the program. */
|
||||
void TypeFailure(JSContext *cx, const char *fmt, ...);
|
||||
|
||||
} /* namespace types */
|
||||
} /* namespace js */
|
||||
|
||||
|
@ -131,7 +131,7 @@ MakeTypeId(jsid id)
|
||||
|
||||
/* Convert an id for printing during debug. */
|
||||
static inline const char *
|
||||
TypeIdString(JSContext *cx, jsid id)
|
||||
TypeIdString(jsid id)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (JSID_IS_VOID(id))
|
||||
@ -339,9 +339,10 @@ JSContext::addTypePropertyId(js::types::TypeObject *obj, jsid id, js::types::jst
|
||||
return;
|
||||
|
||||
if (compartment->types.interpreting) {
|
||||
js::types::InferSpewType(js::types::ISpewDynamic, this, type, "AddBuiltin: %s %s:",
|
||||
js::types::TypeIdString(this, obj->name),
|
||||
js::types::TypeIdString(this, id));
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "AddBuiltin: %s %s: %s",
|
||||
js::types::TypeIdString(obj->name),
|
||||
js::types::TypeIdString(id),
|
||||
js::types::TypeString(type));
|
||||
compartment->types.addDynamicType(this, types, type);
|
||||
} else {
|
||||
types->addType(this, type);
|
||||
@ -373,7 +374,7 @@ JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid seco
|
||||
}
|
||||
|
||||
inline void
|
||||
JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense)
|
||||
JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense, bool dynamic)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
if (notDense) {
|
||||
@ -385,6 +386,12 @@ JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense)
|
||||
}
|
||||
obj->isPackedArray = false;
|
||||
|
||||
if (dynamic) {
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "%s: %s",
|
||||
notDense ? "NonDenseArray" : "NonPackedArray",
|
||||
js::types::TypeIdString(obj->name));
|
||||
}
|
||||
|
||||
/* All constraints listening to changes in packed/dense status are on the element types. */
|
||||
js::types::TypeSet *elementTypes = obj->properties(this).getVariable(this, JSID_VOID);
|
||||
js::types::TypeConstraint *constraint = elementTypes->constraintList;
|
||||
@ -393,34 +400,19 @@ JSContext::markTypeArrayNotPacked(js::types::TypeObject *obj, bool notDense)
|
||||
constraint = constraint->next;
|
||||
}
|
||||
|
||||
if (compartment->types.hasPendingRecompiles())
|
||||
if (dynamic && compartment->types.hasPendingRecompiles())
|
||||
compartment->types.processPendingRecompiles(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
JSContext::monitorTypeObject(js::types::TypeObject *obj)
|
||||
JSContext::markTypeObjectUnknownProperties(js::types::TypeObject *obj)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
if (obj->monitored)
|
||||
if (obj->unknownProperties())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Existing property constraints may have already been added to this object,
|
||||
* which we need to do the right thing for. We can't ensure that we will
|
||||
* mark all monitored objects before they have been accessed, as the __proto__
|
||||
* of a non-monitored object could be dynamically set to a monitored object.
|
||||
* Adding unknown for any properties accessed already accounts for possible
|
||||
* values read from them.
|
||||
*/
|
||||
|
||||
js::types::Variable *var = obj->properties(this).variables;
|
||||
while (var) {
|
||||
var->types.addType(this, js::types::TYPE_UNKNOWN);
|
||||
var = var->next;
|
||||
}
|
||||
|
||||
obj->monitored = true;
|
||||
obj->properties(this).markUnknown(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -463,8 +455,8 @@ JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
|
||||
if (!JSID_IS_VOID(id)) {
|
||||
js::types::TypeSet *types = script->localTypes.getVariable(this, id);
|
||||
if (!types->hasType(type)) {
|
||||
js::types::InferSpewType(js::types::ISpewDynamic, this, type,
|
||||
"AddArg: #%u %u:", script->id, arg);
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "AddArg: #%u %u: %s",
|
||||
script->id, arg, js::types::TypeString(type));
|
||||
compartment->types.addDynamicType(this, types, type);
|
||||
}
|
||||
} else {
|
||||
@ -506,8 +498,8 @@ JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv,
|
||||
else
|
||||
type = js::types::GetValueType(this, thisv);
|
||||
if (!analysis->thisTypes.hasType(type)) {
|
||||
js::types::InferSpewType(js::types::ISpewDynamic, this, type,
|
||||
"AddThis: #%u:", analysis->id);
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
|
||||
analysis->id, js::types::TypeString(type));
|
||||
compartment->types.addDynamicType(this, &analysis->thisTypes, type);
|
||||
}
|
||||
}
|
||||
@ -516,6 +508,8 @@ JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv,
|
||||
compartment->types.interpreting = false;
|
||||
uint64_t startTime = compartment->types.currentTime();
|
||||
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "EntryPoint: #%lu", analysis->id);
|
||||
|
||||
analysis->analyze(this);
|
||||
|
||||
uint64_t endTime = compartment->types.currentTime();
|
||||
@ -606,7 +600,10 @@ JSScript::typeMonitorAssign(JSContext *cx, const jsbytecode *pc,
|
||||
return;
|
||||
}
|
||||
|
||||
cx->compartment->types.dynamicAssign(cx, obj, id, rval);
|
||||
if (!obj->getTypeObject()->unknownProperties() ||
|
||||
id == ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom)) {
|
||||
cx->compartment->types.dynamicAssign(cx, obj, id, rval);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -619,8 +616,9 @@ JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
|
||||
js::types::TypeSet *argTypes = analysis->localTypes.getVariable(cx, id);
|
||||
js::types::jstype type = js::types::GetValueType(cx, value);
|
||||
if (!argTypes->hasType(type)) {
|
||||
js::types::InferSpewType(js::types::ISpewDynamic, cx, type, "SetArgument: #%u %s:",
|
||||
analysis->id, js::types::TypeIdString(cx, id));
|
||||
js::types::InferSpew(js::types::ISpewDynamic, "SetArgument: #%u %s: %s",
|
||||
analysis->id, js::types::TypeIdString(id),
|
||||
js::types::TypeString(type));
|
||||
cx->compartment->types.addDynamicType(cx, argTypes, type);
|
||||
}
|
||||
}
|
||||
@ -727,19 +725,18 @@ Bytecode::getInitObject(JSContext *cx, bool isArray)
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jsid
|
||||
Script::getLocalId(unsigned index, types::TypeStack *stack)
|
||||
Script::getLocalId(unsigned index, Bytecode *code)
|
||||
{
|
||||
if (index >= script->nfixed) {
|
||||
/*
|
||||
* This is an access on a let variable, we need the stack to figure out
|
||||
* the name of the accessed variable. If multiple let variables have
|
||||
* the same name, we flatten their types together.
|
||||
*/
|
||||
stack = stack ? stack->group() : NULL;
|
||||
while (stack && (stack->stackDepth != index - script->nfixed)) {
|
||||
stack = stack->innerStack;
|
||||
stack = stack ? stack->group() : NULL;
|
||||
}
|
||||
if (!code)
|
||||
return JSID_VOID;
|
||||
|
||||
JS_ASSERT(index - script->nfixed < code->stackDepth);
|
||||
unsigned diff = code->stackDepth - (index - script->nfixed);
|
||||
types::TypeStack *stack = code->inStack;
|
||||
for (unsigned i = 1; i < diff; i++)
|
||||
stack = stack->group()->innerStack;
|
||||
JS_ASSERT(stack);
|
||||
|
||||
if (stack && JSID_TO_STRING(stack->letVariable) != NULL)
|
||||
return stack->letVariable;
|
||||
@ -774,19 +771,16 @@ Script::getArgumentId(unsigned index)
|
||||
}
|
||||
|
||||
inline types::TypeSet*
|
||||
Script::getStackTypes(unsigned index, types::TypeStack *stack)
|
||||
Script::getStackTypes(unsigned index, Bytecode *code)
|
||||
{
|
||||
JS_ASSERT(index >= script->nfixed);
|
||||
JS_ASSERT(index - script->nfixed < code->stackDepth);
|
||||
|
||||
stack = stack->group();
|
||||
while (stack && (stack->stackDepth != index - script->nfixed)) {
|
||||
stack = stack->innerStack;
|
||||
stack = stack ? stack->group() : NULL;
|
||||
}
|
||||
|
||||
/* This should not be used for accessing a let variable's stack slot. */
|
||||
JS_ASSERT(stack && !JSID_IS_VOID(stack->letVariable));
|
||||
return &stack->types;
|
||||
types::TypeStack *stack = code->inStack;
|
||||
unsigned diff = code->stackDepth - (index - script->nfixed) - 1;
|
||||
for (unsigned i = 0; i < diff; i++)
|
||||
stack = stack->group()->innerStack;
|
||||
return &stack->group()->types;
|
||||
}
|
||||
|
||||
inline JSValueType
|
||||
@ -826,7 +820,7 @@ TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *
|
||||
JS_ASSERT(this == &cx->compartment->types);
|
||||
JS_ASSERT(type);
|
||||
|
||||
InferSpewType(ISpewOps, cx, type, "pending: C%u", constraint->id());
|
||||
InferSpew(ISpewOps, "pending: C%u %s", constraint->id(), TypeString(type));
|
||||
|
||||
if (pendingCount == pendingCapacity)
|
||||
growPendingArray();
|
||||
@ -852,7 +846,8 @@ TypeCompartment::resolvePending(JSContext *cx)
|
||||
/* Handle all pending type registrations. */
|
||||
while (pendingCount) {
|
||||
const PendingWork &pending = pendingArray[--pendingCount];
|
||||
InferSpewType(ISpewOps, cx, pending.type, "resolve: C%u ", pending.constraint->id());
|
||||
InferSpew(ISpewOps, "resolve: C%u %s",
|
||||
pending.constraint->id(), TypeString(pending.type));
|
||||
pending.constraint->newType(cx, pending.source, pending.type);
|
||||
}
|
||||
|
||||
@ -1061,7 +1056,7 @@ TypeSet::addType(JSContext *cx, jstype type)
|
||||
{
|
||||
JS_ASSERT(type);
|
||||
JS_ASSERT_IF(typeFlags & TYPE_FLAG_UNKNOWN, typeFlags == TYPE_FLAG_UNKNOWN);
|
||||
InferSpewType(ISpewOps, cx, type, "addType: T%u ", id());
|
||||
InferSpew(ISpewOps, "addType: T%u %s", id(), TypeString(type));
|
||||
|
||||
if (typeFlags & TYPE_FLAG_UNKNOWN)
|
||||
return;
|
||||
@ -1124,7 +1119,7 @@ inline TypeSet *
|
||||
TypeSet::make(JSContext *cx, JSArenaPool &pool, const char *name)
|
||||
{
|
||||
TypeSet *res = ArenaNew<TypeSet>(pool, &pool);
|
||||
InferSpew(ISpewOps, "intermediate %s T%u\n", name, res->id());
|
||||
InferSpew(ISpewOps, "intermediate %s T%u", name, res->id());
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -1149,7 +1144,6 @@ TypeStack::setInnerStack(TypeStack *inner)
|
||||
{
|
||||
JS_ASSERT(!mergedGroup);
|
||||
innerStack = inner;
|
||||
stackDepth = inner ? (inner->group()->stackDepth + 1) : 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
@ -1215,8 +1209,17 @@ VariableSet::getVariable(JSContext *cx, jsid id)
|
||||
res->next = variables;
|
||||
variables = res;
|
||||
|
||||
InferSpew(ISpewOps, "addVariable: %s %s T%u\n",
|
||||
TypeIdString(cx, name()), TypeIdString(cx, id), res->types.id());
|
||||
InferSpew(ISpewOps, "addVariable: %s %s T%u",
|
||||
TypeIdString(name()), TypeIdString(id), res->types.id());
|
||||
|
||||
if (unknown) {
|
||||
/*
|
||||
* Immediately mark the variable as unknown. Ideally we won't be doing this
|
||||
* too often, but we don't assert !unknown to avoid extra complexity in
|
||||
* other code accessing variable sets.
|
||||
*/
|
||||
res->types.addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
/* Propagate the variable to any other sets receiving our variables. */
|
||||
if (propagateCount >= 2) {
|
||||
|
@ -482,6 +482,7 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
if (argc <= 1) {
|
||||
vp->setDouble(js_NaN);
|
||||
cx->markTypeCallerOverflow();
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (!ValueToNumber(cx, vp[2], &x))
|
||||
@ -522,6 +523,9 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
|
||||
z = pow(x, y);
|
||||
|
||||
vp->setNumber(z);
|
||||
if (vp->isDouble() && vp[2].isInt32() && vp[3].isInt32())
|
||||
cx->markTypeCallerOverflow();
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -875,7 +879,7 @@ static JSFunctionSpec math_static_methods[] = {
|
||||
JS_TN("log", math_log, 1, 0, &math_log_trcinfo, JS_TypeHandlerFloat),
|
||||
JS_TN("max", js_math_max, 2, 0, &js_math_max_trcinfo, math_TypeArith),
|
||||
JS_TN("min", js_math_min, 2, 0, &js_math_min_trcinfo, math_TypeArith),
|
||||
JS_TN("pow", math_pow, 2, 0, &math_pow_trcinfo, JS_TypeHandlerFloat),
|
||||
JS_TN("pow", math_pow, 2, 0, &math_pow_trcinfo, math_TypeArith),
|
||||
JS_TN("random", math_random, 0, 0, &math_random_trcinfo, JS_TypeHandlerFloat),
|
||||
JS_TN("round", js_math_round, 1, 0, &js_math_round_trcinfo, JS_TypeHandlerInt),
|
||||
JS_TN("sin", math_sin, 1, 0, &math_sin_trcinfo, JS_TypeHandlerFloat),
|
||||
|
@ -1824,7 +1824,7 @@ JSScript::makeAnalysis(JSContext *cx)
|
||||
char name[40];
|
||||
JS_snprintf(name, sizeof(name), "#%u:locals", analysis->id);
|
||||
analysis->localTypes.name_ = ATOM_TO_JSID(js_Atomize(cx, name, strlen(name), 0));
|
||||
types::InferSpew(types::ISpewOps, "newScript: %s\n", name);
|
||||
types::InferSpew(types::ISpewOps, "newScript: %s", name);
|
||||
#endif
|
||||
#endif /* JS_TYPE_INFERENCE */
|
||||
|
||||
|
@ -2469,6 +2469,12 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew)
|
||||
stubcc.linkExitDirect(notCompiled, stubcc.masm.label());
|
||||
stubcc.rejoin(Changes(1));
|
||||
callPatches.append(callPatch);
|
||||
|
||||
if (recompiling) {
|
||||
/* In case we recompiled this call to an uncached call. */
|
||||
OOL_STUBCALL(JS_FUNC_TO_DATA_PTR(void *, callingNew ? ic::New : ic::Call));
|
||||
stubcc.rejoin(Changes(1));
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -5068,8 +5074,7 @@ mjit::Compiler::fixDoubleTypes(Uses uses)
|
||||
}
|
||||
analyze::Bytecode &opinfo = analysis->getCode(PC);
|
||||
for (uint32 i = 0; i < opinfo.stackDepth - uses.nuses; i++) {
|
||||
types::TypeStack *stack = opinfo.inStack;
|
||||
types::TypeSet *types = analysis->getStackTypes(script->nfixed + i, stack);
|
||||
types::TypeSet *types = analysis->getStackTypes(script->nfixed + i, &opinfo);
|
||||
JSValueType type = types->getKnownTypeTag(cx, script);
|
||||
if (type == JSVAL_TYPE_DOUBLE) {
|
||||
FrameEntry *fe = frame.getLocal(script->nfixed + i);
|
||||
|
@ -125,6 +125,8 @@ Recompiler::stealNative(JITScript *jit, jsbytecode *pc)
|
||||
/* Already stole this stub. */
|
||||
PatchableNative native;
|
||||
native.pc = NULL;
|
||||
native.guardedNative = NULL;
|
||||
native.pool = NULL;
|
||||
return native;
|
||||
}
|
||||
|
||||
|
@ -4208,13 +4208,6 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void type_Bailout(JSContext *cx, JSTypeFunction *fun, JSTypeCallsite *site)
|
||||
{
|
||||
#ifdef JS_TYPE_INFERENCE
|
||||
cx->compartment->types.ignoreWarnings = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN_TYPE("version", Version, 0,0, JS_TypeHandlerInt),
|
||||
@ -4267,12 +4260,12 @@ static JSFunctionSpec shell_functions[] = {
|
||||
JS_FN_TYPE("build", BuildDate, 0,0, JS_TypeHandlerVoid),
|
||||
JS_FN_TYPE("clear", Clear, 0,0, JS_TypeHandlerVoid),
|
||||
JS_FN_TYPE("intern", Intern, 1,0, JS_TypeHandlerVoid),
|
||||
JS_FN_TYPE("clone", Clone, 1,0, type_Bailout),
|
||||
JS_FN_TYPE("getpda", GetPDA, 1,0, JS_TypeHandlerMissing),
|
||||
JS_FN_TYPE("clone", Clone, 1,0, JS_TypeHandlerDynamic),
|
||||
JS_FN_TYPE("getpda", GetPDA, 1,0, JS_TypeHandlerDynamic),
|
||||
JS_FN_TYPE("getslx", GetSLX, 1,0, JS_TypeHandlerInt),
|
||||
JS_FN_TYPE("toint32", ToInt32, 1,0, JS_TypeHandlerInt),
|
||||
JS_FN_TYPE("evalcx", EvalInContext, 1,0, type_Bailout),
|
||||
JS_FN_TYPE("evalInFrame", EvalInFrame, 2,0, type_Bailout),
|
||||
JS_FN_TYPE("evalcx", EvalInContext, 1,0, JS_TypeHandlerDynamic),
|
||||
JS_FN_TYPE("evalInFrame", EvalInFrame, 2,0, JS_TypeHandlerDynamic),
|
||||
JS_FN_TYPE("shapeOf", ShapeOf, 1,0, JS_TypeHandlerInt),
|
||||
#ifdef MOZ_SHARK
|
||||
JS_FN_TYPE("startShark", js_StartShark, 0,0, JS_TypeHandlerVoid),
|
||||
@ -4661,7 +4654,7 @@ its_bindMethod(JSContext *cx, uintN argc, jsval *vp)
|
||||
}
|
||||
|
||||
static JSFunctionSpec its_methods[] = {
|
||||
{"bindMethod", its_bindMethod, 2,0, type_Bailout},
|
||||
{"bindMethod", its_bindMethod, 2,0, JS_TypeHandlerMissing},
|
||||
{NULL,NULL,0,0}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user