mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Optimize arguments accesses, bug 658638.
This commit is contained in:
parent
02a6764444
commit
621ab68f21
14
js/src/jit-test/tests/jaeger/argumentsOptimize-1.js
Normal file
14
js/src/jit-test/tests/jaeger/argumentsOptimize-1.js
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
function bar() {
|
||||
foo.arguments.length = 10;
|
||||
}
|
||||
|
||||
function foo(x) {
|
||||
var a = arguments;
|
||||
var n = 0;
|
||||
bar();
|
||||
assertEq(x, 5);
|
||||
assertEq(a.length, 10);
|
||||
}
|
||||
|
||||
foo(5);
|
27
js/src/jit-test/tests/jaeger/argumentsOptimize-2.js
Normal file
27
js/src/jit-test/tests/jaeger/argumentsOptimize-2.js
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
function foo() {
|
||||
var x = 0;
|
||||
for (var i = arguments.length - 1; i >= 0; i--)
|
||||
x += arguments[i];
|
||||
return x;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
var x = 0;
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
x += arguments[i];
|
||||
return x;
|
||||
}
|
||||
|
||||
function baz(a,b,c,d,e) {
|
||||
var x = 0;
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
x += arguments[i];
|
||||
return x;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assertEq(foo(1,2,3,4,5), 15);
|
||||
assertEq(bar(1,2.5,true,{valueOf:function() { return 10}},"five"), "14.5five");
|
||||
assertEq(baz(1,2,3,4,5), 15);
|
||||
}
|
29
js/src/jit-test/tests/jaeger/recompile/bug659766.js
Normal file
29
js/src/jit-test/tests/jaeger/recompile/bug659766.js
Normal file
@ -0,0 +1,29 @@
|
||||
var gTestcases = new Array;
|
||||
var gTc = gTestcases;
|
||||
function TestCase(n, d, e, a) {
|
||||
this.description=d
|
||||
this.reason=''
|
||||
gTestcases[gTc++]=this
|
||||
}
|
||||
TestCase.prototype.dump=function () + toPrinted(this.description) + toPrinted(this.reason) + '\n';
|
||||
function toPrinted(value) value=value.replace(/\\n/g, 'NL').replace(/[^\x20-\x7E]+/g, escapeString);
|
||||
function escapeString (str) {
|
||||
try {
|
||||
err
|
||||
} catch(ex) { }
|
||||
}
|
||||
function jsTestDriverEnd() {
|
||||
for (var i = 0; i < gTestcases.length; i++)
|
||||
gTestcases[i].dump()
|
||||
}
|
||||
var SECTION = "dowhile-007";
|
||||
DoWhile();
|
||||
function DoWhile( object ) result1=false;
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"break one: ",
|
||||
result1
|
||||
);
|
||||
jsTestDriverEnd();
|
||||
new TestCase( SECTION, "'<27>O<EFBFBD> <20>:i<10><>'.match(new RegExp('.+'))", [], '<27>O<EFBFBD> <20>:i<10><>');
|
||||
jsTestDriverEnd();
|
@ -329,6 +329,10 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
isInlineable = false;
|
||||
}
|
||||
|
||||
modifiesArguments_ = false;
|
||||
if (script->nClosedArgs || (script->fun && script->fun->isHeavyweight()))
|
||||
modifiesArguments_ = true;
|
||||
|
||||
canTrackVars = true;
|
||||
|
||||
/*
|
||||
@ -641,15 +645,19 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Additional opcodes which can be compiled but which can't be inlined. */
|
||||
case JSOP_ARGUMENTS:
|
||||
case JSOP_EVAL:
|
||||
case JSOP_FORARG:
|
||||
case JSOP_SETARG:
|
||||
case JSOP_INCARG:
|
||||
case JSOP_DECARG:
|
||||
case JSOP_ARGINC:
|
||||
case JSOP_ARGDEC:
|
||||
modifiesArguments_ = true;
|
||||
isInlineable = false;
|
||||
break;
|
||||
|
||||
/* Additional opcodes which can be compiled but which can't be inlined. */
|
||||
case JSOP_ARGUMENTS:
|
||||
case JSOP_EVAL:
|
||||
case JSOP_THROW:
|
||||
case JSOP_EXCEPTION:
|
||||
case JSOP_DEFLOCALFUN:
|
||||
|
@ -742,6 +742,12 @@ class SSAValue
|
||||
u.var.offset = offset;
|
||||
}
|
||||
|
||||
static SSAValue WrittenVar(uint32 slot, uint32 offset) {
|
||||
SSAValue v;
|
||||
v.initWritten(slot, offset);
|
||||
return v;
|
||||
}
|
||||
|
||||
void initPhi(uint32 offset, SSAPhiNode *node) {
|
||||
clear();
|
||||
u.phi.kind = PHI;
|
||||
@ -864,6 +870,7 @@ class ScriptAnalysis
|
||||
bool canTrackVars;
|
||||
bool isInlineable;
|
||||
uint32 numReturnSites_;
|
||||
bool modifiesArguments_;
|
||||
|
||||
/* Offsets at which each local becomes unconditionally defined, or a value below. */
|
||||
uint32 *definedLocals;
|
||||
@ -906,6 +913,12 @@ class ScriptAnalysis
|
||||
bool hasFunctionCalls() const { return hasCalls; }
|
||||
uint32 numReturnSites() const { return numReturnSites_; }
|
||||
|
||||
/*
|
||||
* True if all named formal arguments are not modified. If the arguments
|
||||
* object cannot escape, the arguments are never modified within the script.
|
||||
*/
|
||||
bool modifiesArguments() { return modifiesArguments_; }
|
||||
|
||||
/* Accessors for bytecode information. */
|
||||
|
||||
Bytecode& getCode(uint32 offset) {
|
||||
@ -1151,6 +1164,8 @@ class ScriptAnalysis
|
||||
|
||||
/* Type inference helpers */
|
||||
bool analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state);
|
||||
bool followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen);
|
||||
bool followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen);
|
||||
inline void setForTypes(JSContext *cx, jsbytecode *pc, types::TypeSet *types);
|
||||
};
|
||||
|
||||
|
@ -223,6 +223,10 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
|
||||
mjit::ReleaseScriptCode(cx, script, true);
|
||||
mjit::ReleaseScriptCode(cx, script, false);
|
||||
script->debugMode = !!debug;
|
||||
|
||||
/* Mark arguments objects as escaping in all scripts if debug mode is on. */
|
||||
if (script->usesArguments && debug)
|
||||
cx->markTypeObjectFlags(script->fun->getType(), types::OBJECT_FLAG_CREATED_ARGUMENTS);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2824,7 +2824,8 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg,
|
||||
/* Try to optimize arguments.length into JSOP_ARGCNT */
|
||||
if (!BindNameToSlot(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
if (pn->pn_atom == cx->runtime->atomState.lengthAtom) {
|
||||
if (!cx->typeInferenceEnabled() &&
|
||||
pn->pn_atom == cx->runtime->atomState.lengthAtom) {
|
||||
if (pn2->pn_op == JSOP_ARGUMENTS)
|
||||
return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;
|
||||
}
|
||||
@ -2916,6 +2917,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT32(next->pn_dval, &slot) &&
|
||||
jsuint(slot) < JS_BIT(16) &&
|
||||
!cx->typeInferenceEnabled() &&
|
||||
(!cg->inStrictMode() ||
|
||||
(!cg->mutatesParameter() && !cg->callsEval()))) {
|
||||
/*
|
||||
@ -2992,6 +2994,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT32(right->pn_dval, &slot) &&
|
||||
jsuint(slot) < JS_BIT(16) &&
|
||||
!cx->typeInferenceEnabled() &&
|
||||
(!cg->inStrictMode() ||
|
||||
(!cg->mutatesParameter() && !cg->callsEval()))) {
|
||||
left->pn_offset = right->pn_offset = top;
|
||||
|
@ -109,6 +109,9 @@ js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp)
|
||||
{
|
||||
JSObject *argsobj;
|
||||
|
||||
cx->markTypeObjectFlags(fp->fun()->getType(),
|
||||
OBJECT_FLAG_CREATED_ARGUMENTS | OBJECT_FLAG_UNINLINEABLE);
|
||||
|
||||
if (fp->hasOverriddenArgs()) {
|
||||
JS_ASSERT(fp->hasCallObj());
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
|
||||
|
@ -175,6 +175,8 @@ types::TypeString(jstype type)
|
||||
return "float";
|
||||
case TYPE_STRING:
|
||||
return "string";
|
||||
case TYPE_LAZYARGS:
|
||||
return "lazyargs";
|
||||
case TYPE_UNKNOWN:
|
||||
return "unknown";
|
||||
default: {
|
||||
@ -200,8 +202,8 @@ types::InferSpew(SpewChannel channel, const char *fmt, ...)
|
||||
}
|
||||
|
||||
/* Whether types can be considered to contain type or an equivalent, for checking results. */
|
||||
static inline bool
|
||||
TypeSetMatches(JSContext *cx, TypeSet *types, jstype type)
|
||||
bool
|
||||
types::TypeMatches(JSContext *cx, TypeSet *types, jstype type)
|
||||
{
|
||||
if (types->hasType(type))
|
||||
return true;
|
||||
@ -253,7 +255,7 @@ types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &val
|
||||
AutoEnterTypeInference enter(cx);
|
||||
|
||||
TypeSet *types = obj->getProperty(cx, id, false);
|
||||
if (types && !TypeSetMatches(cx, types, type)) {
|
||||
if (types && !TypeMatches(cx, types, type)) {
|
||||
TypeFailure(cx, "Missing type in object %s %s: %s",
|
||||
obj->name(), TypeIdString(id), TypeString(type));
|
||||
}
|
||||
@ -291,7 +293,7 @@ TypeSet::addTypeSet(JSContext *cx, ClonedTypeSet *types)
|
||||
return;
|
||||
}
|
||||
|
||||
for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
|
||||
for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
|
||||
if (types->typeFlags & (1 << type))
|
||||
addType(cx, type);
|
||||
}
|
||||
@ -334,7 +336,7 @@ TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
|
||||
return;
|
||||
}
|
||||
|
||||
for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
|
||||
for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
|
||||
if (typeFlags & (1 << type))
|
||||
cx->compartment->types.addPending(cx, constraint, this, type);
|
||||
}
|
||||
@ -380,6 +382,8 @@ TypeSet::print(JSContext *cx)
|
||||
printf(" float");
|
||||
if (typeFlags & TYPE_FLAG_STRING)
|
||||
printf(" string");
|
||||
if (typeFlags & TYPE_FLAG_LAZYARGS)
|
||||
printf(" lazyargs");
|
||||
|
||||
if (objectCount) {
|
||||
printf(" object[%u]", objectCount);
|
||||
@ -783,6 +787,35 @@ TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeS
|
||||
add(cx, ArenaNew<TypeConstraintSubsetBarrier>(cx->compartment->pool, script, pc, target));
|
||||
}
|
||||
|
||||
/*
|
||||
* Constraint which marks a pushed ARGUMENTS value as unknown if the script has
|
||||
* an arguments object created in the future.
|
||||
*/
|
||||
class TypeConstraintLazyArguments : public TypeConstraint
|
||||
{
|
||||
public:
|
||||
jsbytecode *pc;
|
||||
TypeSet *target;
|
||||
|
||||
TypeConstraintLazyArguments(JSScript *script, TypeSet *target)
|
||||
: TypeConstraint("lazyArgs", script), target(target)
|
||||
{}
|
||||
|
||||
void newType(JSContext *cx, TypeSet *source, jstype type) {}
|
||||
|
||||
void newObjectState(JSContext *cx, TypeObject *object, bool force)
|
||||
{
|
||||
if (object->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS))
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
TypeSet::addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target)
|
||||
{
|
||||
add(cx, ArenaNew<TypeConstraintLazyArguments>(cx->compartment->pool, script, target));
|
||||
}
|
||||
|
||||
/*
|
||||
* Type constraint which marks the result of 'for in' loops as unknown if the
|
||||
* iterated value could be a generator.
|
||||
@ -860,6 +893,15 @@ GetPropertyObject(JSContext *cx, JSScript *script, jstype type)
|
||||
return object;
|
||||
}
|
||||
|
||||
static inline void
|
||||
MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
|
||||
{
|
||||
if (CanHaveReadBarrier(pc))
|
||||
script->analysis(cx)->addTypeBarrier(cx, pc, target, TYPE_UNKNOWN);
|
||||
else
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a property access on a specific object. All property accesses go through
|
||||
* here, whether via x.f, x[f], or global name accesses.
|
||||
@ -888,7 +930,7 @@ PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *obje
|
||||
/* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
|
||||
if (object->unknownProperties()) {
|
||||
if (!assign)
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
MarkPropertyAccessUnknown(cx, script, pc, target);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -915,6 +957,8 @@ PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *obje
|
||||
void
|
||||
TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
{
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
|
||||
if (type == TYPE_UNKNOWN || (!TypeIsObject(type) && !script->global)) {
|
||||
/*
|
||||
* Access on an unknown object. Reads produce an unknown result, writes
|
||||
@ -925,13 +969,24 @@ TypeConstraintProp::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
if (assign)
|
||||
cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
|
||||
else
|
||||
target->addType(cx, TYPE_UNKNOWN);
|
||||
MarkPropertyAccessUnknown(cx, script, pc, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == TYPE_LAZYARGS) {
|
||||
/* Catch cases which will be accounted for by the followEscapingArguments analysis. */
|
||||
if (assign || (id != JSID_VOID && id != id_length(cx)))
|
||||
return;
|
||||
|
||||
if (id == JSID_VOID)
|
||||
MarkPropertyAccessUnknown(cx, script, pc, target);
|
||||
else
|
||||
target->addType(cx, TYPE_INT32);
|
||||
return;
|
||||
}
|
||||
|
||||
TypeObject *object = GetPropertyObject(cx, script, type);
|
||||
if (object) {
|
||||
UntrapOpcode untrap(cx, script, pc);
|
||||
PropertyAccess(cx, script, pc, object, assign, target, id);
|
||||
|
||||
if (!object->unknownProperties() &&
|
||||
@ -1072,7 +1127,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
|
||||
return;
|
||||
|
||||
/* Analyze the function if we have not already done so. */
|
||||
if (!callee->analyzed) {
|
||||
if (!callee->ranInference) {
|
||||
ScriptAnalysis *calleeAnalysis = callee->analysis(cx);
|
||||
if (!calleeAnalysis) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
@ -1381,6 +1436,8 @@ GetValueTypeFromTypeFlags(TypeFlags flags)
|
||||
return JSVAL_TYPE_DOUBLE;
|
||||
case TYPE_FLAG_STRING:
|
||||
return JSVAL_TYPE_STRING;
|
||||
case TYPE_FLAG_LAZYARGS:
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
default:
|
||||
return JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
@ -1541,6 +1598,43 @@ TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
FixLazyArguments(JSContext *cx, JSScript *script)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
mjit::ExpandInlineFrames(cx, FRAME_EXPAND_ALL);
|
||||
#endif
|
||||
|
||||
/* :FIXME: handle OOM at calls here. */
|
||||
ScriptAnalysis *analysis = script->analysis(cx);
|
||||
if (analysis && !analysis->ranBytecode())
|
||||
analysis->analyzeBytecode(cx);
|
||||
if (!analysis || analysis->OOM())
|
||||
return;
|
||||
|
||||
for (AllFramesIter iter(cx); !iter.done(); ++iter) {
|
||||
StackFrame *fp = iter.fp();
|
||||
if (fp->isScriptFrame() && fp->script() == script) {
|
||||
JSInlinedSite *inline_;
|
||||
jsbytecode *pc = fp->pc(cx, NULL, &inline_);
|
||||
JS_ASSERT(!inline_);
|
||||
|
||||
/*
|
||||
* Check locals and stack slots, assignment to individual arguments
|
||||
* is treated as an escape on the arguments.
|
||||
*/
|
||||
Value *sp = fp->base() + analysis->getCode(pc).stackDepth;
|
||||
for (Value *vp = fp->slots(); vp < sp; vp++) {
|
||||
if (vp->isMagicCheck(JS_LAZY_ARGUMENTS)) {
|
||||
if (!js_GetArgsValue(cx, fp, vp)) {
|
||||
/* FIXME */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force)
|
||||
{
|
||||
@ -1552,8 +1646,18 @@ ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool f
|
||||
if (!elementTypes)
|
||||
return;
|
||||
if (markingUnknown) {
|
||||
JSScript *fixArgsScript = NULL;
|
||||
if (!(object->flags & OBJECT_FLAG_CREATED_ARGUMENTS) && object->isFunction) {
|
||||
TypeFunction *fun = object->asFunction();
|
||||
if (fun->script && fun->script->usedLazyArgs)
|
||||
fixArgsScript = fun->script;
|
||||
}
|
||||
|
||||
/* Mark as unknown after getting the element types, to avoid assertion. */
|
||||
object->flags = OBJECT_FLAG_UNKNOWN_MASK;
|
||||
|
||||
if (fixArgsScript)
|
||||
FixLazyArguments(cx, fixArgsScript);
|
||||
}
|
||||
|
||||
TypeConstraint *constraint = elementTypes->constraintList;
|
||||
@ -2035,7 +2139,7 @@ TypeCompartment::dynamicPush(JSContext *cx, JSScript *script, uint32 offset, jst
|
||||
if (script->hasAnalysis() && script->analysis(cx)->ranInference()) {
|
||||
TypeSet *pushed = script->analysis(cx)->pushedTypes(offset, 0);
|
||||
pushed->addType(cx, type);
|
||||
} else if (script->analyzed) {
|
||||
} else if (script->ranInference) {
|
||||
/* Any new dynamic result triggers reanalysis and recompilation. */
|
||||
ScriptAnalysis *analysis = script->analysis(cx);
|
||||
if (!analysis) {
|
||||
@ -2754,8 +2858,18 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
JS_ASSERT((this->flags & flags) != flags);
|
||||
|
||||
JSScript *fixArgsScript = NULL;
|
||||
if ((flags & ~this->flags & OBJECT_FLAG_CREATED_ARGUMENTS) && isFunction) {
|
||||
TypeFunction *fun = asFunction();
|
||||
if (fun->script && fun->script->usedLazyArgs)
|
||||
fixArgsScript = fun->script;
|
||||
}
|
||||
|
||||
this->flags |= flags;
|
||||
|
||||
if (fixArgsScript)
|
||||
FixLazyArguments(cx, fixArgsScript);
|
||||
|
||||
InferSpew(ISpewOps, "%s: setFlags %u", name(), flags);
|
||||
|
||||
ObjectStateChange(cx, this, false, false);
|
||||
@ -3420,14 +3534,20 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_ARGSUB:
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
break;
|
||||
|
||||
case JSOP_ARGUMENTS:
|
||||
case JSOP_ARGCNT:
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
case JSOP_ARGUMENTS: {
|
||||
/* Compute a precise type only when we know the arguments won't escape. */
|
||||
TypeObject *funType = script->fun->getType();
|
||||
if (funType->unknownProperties() || funType->hasAnyFlags(OBJECT_FLAG_CREATED_ARGUMENTS)) {
|
||||
pushed[0].addType(cx, TYPE_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
TypeSet *prop = funType->getProperty(cx, JSID_VOID, false);
|
||||
if (!prop)
|
||||
break;
|
||||
prop->addLazyArguments(cx, script, &pushed[0]);
|
||||
pushed[0].addType(cx, TYPE_LAZYARGS);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_SETPROP:
|
||||
case JSOP_SETMETHOD: {
|
||||
@ -3886,7 +4006,7 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
|
||||
return;
|
||||
}
|
||||
|
||||
if (script->analyzed) {
|
||||
if (script->ranInference) {
|
||||
/*
|
||||
* Reanalyzing this script after discarding from GC.
|
||||
* Discard/recompile any JIT code for this script,
|
||||
@ -3896,7 +4016,7 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
|
||||
}
|
||||
|
||||
/* Future OOM failures need to setPendingNukeTypes. */
|
||||
script->analyzed = true;
|
||||
script->ranInference = true;
|
||||
|
||||
/*
|
||||
* Set this early to avoid reentrance. Any failures are OOMs, and will nuke
|
||||
@ -3946,6 +4066,126 @@ ScriptAnalysis::analyzeTypes(JSContext *cx)
|
||||
result->replay(cx, script);
|
||||
result = result->next;
|
||||
}
|
||||
|
||||
if (!script->usesArguments)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Do additional analysis to determine whether the arguments object in the
|
||||
* script can escape.
|
||||
*/
|
||||
|
||||
if (script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note: don't check for strict mode code here, even though arguments
|
||||
* accesses in such scripts will always be deoptimized. These scripts can
|
||||
* have a JSOP_ARGUMENTS in their prologue which the usesArguments check
|
||||
* above does not account for. We filter in the interpreter and JITs
|
||||
* themselves.
|
||||
*/
|
||||
if (script->fun->isHeavyweight() || cx->compartment->debugMode) {
|
||||
cx->markTypeObjectFlags(script->fun->getType(),
|
||||
types::OBJECT_FLAG_CREATED_ARGUMENTS);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
while (offset < script->length) {
|
||||
Bytecode *code = maybeCode(offset);
|
||||
jsbytecode *pc = script->code + offset;
|
||||
|
||||
if (code && JSOp(*pc) == JSOP_ARGUMENTS) {
|
||||
Vector<SSAValue> seen(cx);
|
||||
if (!followEscapingArguments(cx, SSAValue::PushedValue(offset, 0), &seen)) {
|
||||
cx->markTypeObjectFlags(script->fun->getType(),
|
||||
types::OBJECT_FLAG_CREATED_ARGUMENTS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
offset += GetBytecodeLength(pc);
|
||||
}
|
||||
|
||||
/*
|
||||
* The VM is now free to use the arguments in this script lazily. If we end
|
||||
* up creating an arguments object for the script in the future or regard
|
||||
* the arguments as escaping, we need to walk the stack and replace lazy
|
||||
* arguments objects with actual arguments objects.
|
||||
*/
|
||||
script->usedLazyArgs = true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptAnalysis::followEscapingArguments(JSContext *cx, const SSAValue &v, Vector<SSAValue> *seen)
|
||||
{
|
||||
/*
|
||||
* trackUseChain is false for initial values of variables, which
|
||||
* cannot hold the script's arguments object.
|
||||
*/
|
||||
if (!trackUseChain(v))
|
||||
return true;
|
||||
|
||||
for (unsigned i = 0; i < seen->length(); i++) {
|
||||
if (v.equals((*seen)[i]))
|
||||
return true;
|
||||
}
|
||||
if (!seen->append(v)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
SSAUseChain *use = useChain(v);
|
||||
while (use) {
|
||||
if (!followEscapingArguments(cx, use, seen))
|
||||
return false;
|
||||
use = use->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<SSAValue> *seen)
|
||||
{
|
||||
if (!use->popped) {
|
||||
for (unsigned i = 0; i < use->u.phi->length; i++) {
|
||||
const SSAValue &v = use->u.phi->options[i];
|
||||
if (!followEscapingArguments(cx, v, seen))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
jsbytecode *pc = script->code + use->offset;
|
||||
uint32 which = use->u.which;
|
||||
|
||||
/* Allow GETELEM and LENGTH on arguments objects that don't escape. */
|
||||
|
||||
/*
|
||||
* Note: if the element index is not an integer we will mark the arguments
|
||||
* as escaping at the access site.
|
||||
*/
|
||||
if (JSOp(*pc) == JSOP_GETELEM && which == 1)
|
||||
return true;
|
||||
|
||||
if (JSOp(*pc) == JSOP_LENGTH)
|
||||
return true;
|
||||
|
||||
/* Allow assignments to non-closed locals (but not arguments). */
|
||||
|
||||
if (JSOp(*pc) == JSOP_SETLOCAL) {
|
||||
uint32 slot = GetBytecodeSlot(script, pc);
|
||||
if (slotEscapes(slot))
|
||||
return false;
|
||||
return followEscapingArguments(cx, SSAValue::WrittenVar(slot, use->offset), seen);
|
||||
}
|
||||
|
||||
if (JSOp(*pc) == JSOP_GETLOCAL)
|
||||
return followEscapingArguments(cx, SSAValue::PushedValue(use->offset, 0), seen);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -4329,7 +4569,7 @@ ScriptAnalysis::printTypes(JSContext *cx)
|
||||
}
|
||||
|
||||
unsigned typeCount = types->getObjectCount() ? 1 : 0;
|
||||
for (jstype type = TYPE_UNDEFINED; type <= TYPE_STRING; type++) {
|
||||
for (jstype type = TYPE_UNDEFINED; type < TYPE_UNKNOWN; type++) {
|
||||
if (types->hasAnyFlag(1 << type))
|
||||
typeCount++;
|
||||
}
|
||||
@ -4590,7 +4830,7 @@ JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value
|
||||
|
||||
jstype type = GetValueType(cx, val);
|
||||
|
||||
if (!TypeSetMatches(cx, types, type)) {
|
||||
if (!types::TypeMatches(cx, types, type)) {
|
||||
TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
|
||||
id(), pc - code, i, TypeString(type));
|
||||
}
|
||||
|
@ -76,12 +76,13 @@ const jstype TYPE_BOOLEAN = 3;
|
||||
const jstype TYPE_INT32 = 4;
|
||||
const jstype TYPE_DOUBLE = 5;
|
||||
const jstype TYPE_STRING = 6;
|
||||
const jstype TYPE_LAZYARGS = 7;
|
||||
|
||||
/*
|
||||
* Aggregate unknown type, could be anything. Typically used when a type set
|
||||
* becomes polymorphic, or when accessing an object with unknown properties.
|
||||
*/
|
||||
const jstype TYPE_UNKNOWN = 7;
|
||||
const jstype TYPE_UNKNOWN = 8;
|
||||
|
||||
/*
|
||||
* Test whether a type is an primitive or an object. Object types can be
|
||||
@ -218,37 +219,38 @@ enum {
|
||||
TYPE_FLAG_INT32 = 1 << TYPE_INT32,
|
||||
TYPE_FLAG_DOUBLE = 1 << TYPE_DOUBLE,
|
||||
TYPE_FLAG_STRING = 1 << TYPE_STRING,
|
||||
TYPE_FLAG_LAZYARGS = 1 << TYPE_LAZYARGS,
|
||||
|
||||
TYPE_FLAG_UNKNOWN = 1 << TYPE_UNKNOWN,
|
||||
|
||||
/* Flag for type sets which are cleared on GC. */
|
||||
TYPE_FLAG_INTERMEDIATE_SET = 0x0100,
|
||||
TYPE_FLAG_INTERMEDIATE_SET = 0x0200,
|
||||
|
||||
/* Flags for type sets which are on object properties. */
|
||||
|
||||
/* Whether this property has ever been directly written. */
|
||||
TYPE_FLAG_OWN_PROPERTY = 0x0200,
|
||||
TYPE_FLAG_OWN_PROPERTY = 0x0400,
|
||||
|
||||
/*
|
||||
* Whether the property has ever been deleted or reconfigured to behave
|
||||
* differently from a normal native property (e.g. made non-writable or
|
||||
* given a scripted getter or setter).
|
||||
*/
|
||||
TYPE_FLAG_CONFIGURED_PROPERTY = 0x0400,
|
||||
TYPE_FLAG_CONFIGURED_PROPERTY = 0x0800,
|
||||
|
||||
/*
|
||||
* Whether the property is definitely in a particular inline slot on all
|
||||
* objects from which it has not been deleted or reconfigured. Implies
|
||||
* OWN_PROPERTY and unlike OWN/CONFIGURED property, this cannot change.
|
||||
*/
|
||||
TYPE_FLAG_DEFINITE_PROPERTY = 0x0800,
|
||||
TYPE_FLAG_DEFINITE_PROPERTY = 0x08000,
|
||||
|
||||
/* If the property is definite, mask and shift storing the slot. */
|
||||
TYPE_FLAG_DEFINITE_MASK = 0xf000,
|
||||
TYPE_FLAG_DEFINITE_SHIFT = 12,
|
||||
TYPE_FLAG_DEFINITE_MASK = 0xf0000,
|
||||
TYPE_FLAG_DEFINITE_SHIFT = 16,
|
||||
|
||||
/* Mask of non-type flags on a type set. */
|
||||
TYPE_FLAG_BASE_MASK = 0xffffff00
|
||||
TYPE_FLAG_BASE_MASK = 0xffffffff ^ ((TYPE_FLAG_UNKNOWN << 1) - 1)
|
||||
};
|
||||
typedef uint32 TypeFlags;
|
||||
|
||||
@ -272,14 +274,17 @@ enum {
|
||||
/* Whether any objects this represents are not packed arrays. */
|
||||
OBJECT_FLAG_NON_PACKED_ARRAY = 1 << 1,
|
||||
|
||||
/* Whether any objects this represents have had their .arguments accessed. */
|
||||
OBJECT_FLAG_UNINLINEABLE = 1 << 2,
|
||||
/* Whether any represented script has had arguments objects created. */
|
||||
OBJECT_FLAG_CREATED_ARGUMENTS = 1 << 2,
|
||||
|
||||
/* Whether any objects this represents have an equality hook. */
|
||||
OBJECT_FLAG_SPECIAL_EQUALITY = 1 << 3,
|
||||
/* Whether any represented script is considered uninlineable. */
|
||||
OBJECT_FLAG_UNINLINEABLE = 1 << 3,
|
||||
|
||||
/* Whether any objects this represents have been iterated over. */
|
||||
OBJECT_FLAG_ITERATED = 1 << 4
|
||||
/* Whether any objects have an equality hook. */
|
||||
OBJECT_FLAG_SPECIAL_EQUALITY = 1 << 4,
|
||||
|
||||
/* Whether any objects have been iterated over. */
|
||||
OBJECT_FLAG_ITERATED = 1 << 5
|
||||
};
|
||||
typedef uint32 TypeObjectFlags;
|
||||
|
||||
@ -370,6 +375,7 @@ class TypeSet
|
||||
void addFilterPrimitives(JSContext *cx, JSScript *script,
|
||||
TypeSet *target, bool onlyNullVoid);
|
||||
void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
|
||||
void addLazyArguments(JSContext *cx, JSScript *script, TypeSet *target);
|
||||
|
||||
void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
|
||||
bool addCondensed(JSContext *cx, JSScript *script);
|
||||
@ -393,6 +399,8 @@ class TypeSet
|
||||
/* Get any type tag which all values in this set must have. */
|
||||
JSValueType getKnownTypeTag(JSContext *cx);
|
||||
|
||||
bool isLazyArguments(JSContext *cx) { return getKnownTypeTag(cx) == JSVAL_TYPE_MAGIC; }
|
||||
|
||||
/* Whether the type set or a particular object has any of a set of flags. */
|
||||
bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);
|
||||
static bool HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags);
|
||||
@ -783,6 +791,12 @@ struct TypeCompartment
|
||||
/* Object to use throughout the compartment as the default type of objects with no prototype. */
|
||||
TypeObject typeEmpty;
|
||||
|
||||
/*
|
||||
* Placeholder object added in type sets throughout the compartment to
|
||||
* represent lazy arguments objects.
|
||||
*/
|
||||
TypeObject typeLazyArguments;
|
||||
|
||||
/*
|
||||
* Bit set if all current types must be marked as unknown, and all scripts
|
||||
* recompiled. Caused by OOM failure within inference operations.
|
||||
@ -903,6 +917,13 @@ enum SpewChannel {
|
||||
void InferSpew(SpewChannel which, const char *fmt, ...);
|
||||
const char * TypeString(jstype type);
|
||||
|
||||
/*
|
||||
* Check that a type set contains value. Unlike TypeSet::hasType, this returns
|
||||
* true if type has had its prototype mutated and another object with unknown
|
||||
* properties is in the type set.
|
||||
*/
|
||||
bool TypeMatches(JSContext *cx, TypeSet *types, jstype type);
|
||||
|
||||
/* Check that the type property for id in obj contains value. */
|
||||
bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value);
|
||||
|
||||
|
@ -73,6 +73,14 @@ GetValueType(JSContext *cx, const Value &val)
|
||||
return TYPE_STRING;
|
||||
case JSVAL_TYPE_NULL:
|
||||
return TYPE_NULL;
|
||||
case JSVAL_TYPE_MAGIC:
|
||||
switch (val.whyMagic()) {
|
||||
case JS_LAZY_ARGUMENTS:
|
||||
return TYPE_LAZYARGS;
|
||||
default:
|
||||
JS_NOT_REACHED("Unknown value");
|
||||
return (jstype) 0;
|
||||
}
|
||||
case JSVAL_TYPE_OBJECT: {
|
||||
JSObject *obj = &val.toObject();
|
||||
JS_ASSERT(obj->type);
|
||||
@ -752,7 +760,7 @@ JSScript::typeSetNewCalled(JSContext *cx)
|
||||
* of 'this' when the script is analyzed or reanalyzed after an invoke with 'new',
|
||||
* and if 'new' is first invoked after the script has already been analyzed.
|
||||
*/
|
||||
if (analyzed) {
|
||||
if (ranInference) {
|
||||
/* Regenerate types for the function. */
|
||||
js::types::AutoEnterTypeInference enter(cx);
|
||||
js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
|
||||
|
@ -4117,6 +4117,10 @@ BEGIN_CASE(JSOP_LENGTH)
|
||||
rval = Int32Value(vp->toString()->length());
|
||||
break;
|
||||
}
|
||||
if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
|
||||
rval = Int32Value(regs.fp()->numActualArgs());
|
||||
break;
|
||||
}
|
||||
if (vp->isObject()) {
|
||||
JSObject *obj = &vp->toObject();
|
||||
if (obj->isArray()) {
|
||||
@ -4459,6 +4463,19 @@ BEGIN_CASE(JSOP_GETELEM)
|
||||
}
|
||||
}
|
||||
|
||||
if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
|
||||
if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
|
||||
regs.sp--;
|
||||
regs.sp[-1] = regs.fp()->canonicalActualArg(rref.toInt32());
|
||||
script->typeMonitor(cx, regs.pc, regs.sp[-1]);
|
||||
len = JSOP_GETELEM_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
cx->markTypeObjectFlags(script->fun->getType(),
|
||||
types::OBJECT_FLAG_CREATED_ARGUMENTS);
|
||||
JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
|
||||
}
|
||||
|
||||
JSObject *obj;
|
||||
VALUE_TO_OBJECT(cx, &lref, obj);
|
||||
|
||||
@ -5157,8 +5174,24 @@ BEGIN_CASE(JSOP_TRAP)
|
||||
BEGIN_CASE(JSOP_ARGUMENTS)
|
||||
{
|
||||
Value rval;
|
||||
if (!js_GetArgsValue(cx, regs.fp(), &rval))
|
||||
goto error;
|
||||
if (cx->typeInferenceEnabled() && !script->strictModeCode) {
|
||||
analyze::ScriptAnalysis *analysis = script->analysis(cx);
|
||||
if (analysis && !analysis->ranInference()) {
|
||||
AutoEnterTypeInference enter(cx);
|
||||
analysis->analyzeTypes(cx);
|
||||
}
|
||||
if (!analysis || analysis->OOM())
|
||||
goto error;
|
||||
if (script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
|
||||
if (!js_GetArgsValue(cx, regs.fp(), &rval))
|
||||
goto error;
|
||||
} else {
|
||||
rval = MagicValue(JS_LAZY_ARGUMENTS);
|
||||
}
|
||||
} else {
|
||||
if (!js_GetArgsValue(cx, regs.fp(), &rval))
|
||||
goto error;
|
||||
}
|
||||
PUSH_COPY(rval);
|
||||
}
|
||||
END_CASE(JSOP_ARGUMENTS)
|
||||
|
@ -465,7 +465,8 @@ struct JSScript {
|
||||
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
|
||||
bool isUncachedEval:1; /* script came from EvaluateScript */
|
||||
bool calledWithNew:1; /* script has been called using 'new' */
|
||||
bool analyzed:1; /* script has been analyzed by type inference */
|
||||
bool usedLazyArgs:1; /* script has used lazy arguments at some point */
|
||||
bool ranInference:1; /* script has been analyzed by type inference */
|
||||
#ifdef JS_METHODJIT
|
||||
bool debugMode:1; /* script was compiled in debug mode */
|
||||
bool singleStepMode:1; /* compile script in single-step mode */
|
||||
|
@ -266,6 +266,7 @@ typedef enum JSWhyMagic
|
||||
JS_THIS_POISON, /* used in debug builds to catch tracing errors */
|
||||
JS_ARG_POISON, /* used in debug builds to catch tracing errors */
|
||||
JS_SERIALIZE_NO_NODE, /* an empty subnode in the AST serializer */
|
||||
JS_LAZY_ARGUMENTS, /* lazy arguments value on the stack */
|
||||
JS_GENERIC_MAGIC /* for local use */
|
||||
} JSWhyMagic;
|
||||
|
||||
|
@ -539,6 +539,11 @@ class Value
|
||||
return JSVAL_IS_MAGIC_IMPL(data);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE
|
||||
bool isMagicCheck(JSWhyMagic why) const {
|
||||
return isMagic() && data.s.payload.why == why;
|
||||
}
|
||||
|
||||
#if JS_BITS_PER_WORD == 64
|
||||
JS_ALWAYS_INLINE
|
||||
bool hasPtrPayload() const {
|
||||
@ -557,13 +562,11 @@ class Value
|
||||
return JSVAL_TRACE_KIND_IMPL(data);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_ALWAYS_INLINE
|
||||
JSWhyMagic whyMagic() const {
|
||||
JS_ASSERT(isMagic());
|
||||
return data.s.payload.why;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*** Comparison ***/
|
||||
|
||||
|
@ -785,6 +785,20 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist
|
||||
add32(Imm32(delta), key.reg());
|
||||
}
|
||||
|
||||
void loadFrameActuals(JSFunction *fun, RegisterID reg) {
|
||||
/* Bias for the case where there was an arguments overflow. */
|
||||
load32(Address(JSFrameReg, StackFrame::offsetOfArgs()), reg);
|
||||
add32(Imm32(fun->nargs + 2), reg);
|
||||
Jump overflowArgs = branchTest32(Assembler::NonZero,
|
||||
Address(JSFrameReg, StackFrame::offsetOfFlags()),
|
||||
Imm32(StackFrame::OVERFLOW_ARGS));
|
||||
move(Imm32(fun->nargs), reg);
|
||||
overflowArgs.linkTo(label(), this);
|
||||
lshift32(Imm32(3), reg);
|
||||
neg32(reg);
|
||||
addPtr(JSFrameReg, reg);
|
||||
}
|
||||
|
||||
void loadObjClass(RegisterID objReg, RegisterID destReg) {
|
||||
loadPtr(Address(objReg, offsetof(JSObject, clasp)), destReg);
|
||||
}
|
||||
|
@ -787,6 +787,22 @@ mjit::Compiler::generatePrologue()
|
||||
masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
|
||||
hasScope.linkTo(masm.label(), &masm);
|
||||
}
|
||||
|
||||
if (outerScript->usesArguments && !script->fun->isHeavyweight()) {
|
||||
/*
|
||||
* Make sure that fp->args.nactual is always coherent. This may be
|
||||
* inspected directly by JIT code, and is not guaranteed to be
|
||||
* correct if the UNDERFLOW and OVERFLOW flags are not set.
|
||||
*/
|
||||
Jump hasArgs = masm.branchTest32(Assembler::NonZero, FrameFlagsAddress(),
|
||||
Imm32(StackFrame::OVERRIDE_ARGS |
|
||||
StackFrame::UNDERFLOW_ARGS |
|
||||
StackFrame::OVERFLOW_ARGS |
|
||||
StackFrame::HAS_ARGS_OBJ));
|
||||
masm.store32(Imm32(script->fun->nargs),
|
||||
Address(JSFrameReg, StackFrame::offsetOfArgs()));
|
||||
hasArgs.linkTo(masm.label(), &masm);
|
||||
}
|
||||
}
|
||||
|
||||
if (isConstructing)
|
||||
@ -1619,11 +1635,16 @@ mjit::Compiler::generateMethod()
|
||||
* 'apply' actually refers to js_fun_apply. If this is not true,
|
||||
* the slow path in JSOP_FUNAPPLY will create the args object.
|
||||
*/
|
||||
if (canUseApplyTricks())
|
||||
if (canUseApplyTricks()) {
|
||||
applyTricks = LazyArgsObj;
|
||||
else
|
||||
jsop_arguments();
|
||||
pushSyncedEntry(0);
|
||||
pushSyncedEntry(0);
|
||||
} else if (cx->typeInferenceEnabled() && !script->strictModeCode &&
|
||||
!script->fun->getType()->hasAnyFlags(types::OBJECT_FLAG_CREATED_ARGUMENTS)) {
|
||||
frame.push(MagicValue(JS_LAZY_ARGUMENTS));
|
||||
} else {
|
||||
jsop_arguments(REJOIN_FALLTHROUGH);
|
||||
pushSyncedEntry(0);
|
||||
}
|
||||
END_CASE(JSOP_ARGUMENTS)
|
||||
|
||||
BEGIN_CASE(JSOP_FORARG)
|
||||
@ -3401,7 +3422,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew, FrameSize
|
||||
#endif
|
||||
if (applyTricks == LazyArgsObj) {
|
||||
/* frame.pop() above reset us to pre-JSOP_ARGUMENTS state */
|
||||
jsop_arguments();
|
||||
jsop_arguments(REJOIN_RESUME);
|
||||
frame.pushSynced(JSVAL_TYPE_UNKNOWN);
|
||||
}
|
||||
emitUncachedCall(callImmArgc, callingNew);
|
||||
@ -4170,19 +4191,8 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the incoming type will never PIC, take slow path. */
|
||||
if (top->isNotType(JSVAL_TYPE_OBJECT)) {
|
||||
jsop_getprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
frame.forgetMismatchedObject(top);
|
||||
|
||||
if (JSOp(*PC) == JSOP_LENGTH && cx->typeInferenceEnabled() && !hasTypeBarriers(PC)) {
|
||||
/*
|
||||
* Check if this is an array we can make a loop invariant entry for.
|
||||
* This will fail for objects which are not definitely dense arrays.
|
||||
*/
|
||||
/* Check if this is an array we can make a loop invariant entry for. */
|
||||
if (loop && loop->generatingInvariants()) {
|
||||
CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0));
|
||||
FrameEntry *fe = loop->invariantLength(topv);
|
||||
@ -4194,12 +4204,13 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType,
|
||||
}
|
||||
}
|
||||
|
||||
types::TypeSet *types = analysis->poppedTypes(PC, 0);
|
||||
|
||||
/*
|
||||
* Check if we are accessing the 'length' property of a known dense array.
|
||||
* Note that if the types are known to indicate dense arrays, their lengths
|
||||
* must fit in an int32.
|
||||
*/
|
||||
types::TypeSet *types = analysis->poppedTypes(PC, 0);
|
||||
if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
|
||||
bool isObject = top->isTypeKnown();
|
||||
if (!isObject) {
|
||||
@ -4215,8 +4226,27 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, JSValueType knownType,
|
||||
stubcc.rejoin(Changes(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we are accessing the 'length' of the lazy arguments for the
|
||||
* current frame. No actual arguments object has ever been constructed
|
||||
* for the script, so we can go straight to nactual.
|
||||
*/
|
||||
if (types->isLazyArguments(cx)) {
|
||||
frame.pop();
|
||||
frame.push(Address(JSFrameReg, StackFrame::offsetOfArgs()), JSVAL_TYPE_INT32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the incoming type will never PIC, take slow path. */
|
||||
if (top->isNotType(JSVAL_TYPE_OBJECT)) {
|
||||
jsop_getprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
frame.forgetMismatchedObject(top);
|
||||
|
||||
/* Check if this is a property access we can make a loop invariant entry for. */
|
||||
if (loop && loop->generatingInvariants() && !hasTypeBarriers(PC)) {
|
||||
CrossSSAValue topv(a->inlineIndex, analysis->poppedValue(PC, 0));
|
||||
@ -6307,10 +6337,10 @@ mjit::Compiler::emitEval(uint32 argc)
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_arguments()
|
||||
mjit::Compiler::jsop_arguments(RejoinState rejoin)
|
||||
{
|
||||
prepareStubCall(Uses(0));
|
||||
INLINE_STUBCALL(stubs::Arguments, REJOIN_NONE);
|
||||
INLINE_STUBCALL(stubs::Arguments, rejoin);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -654,7 +654,7 @@ class Compiler : public BaseCompiler
|
||||
void enterBlock(JSObject *obj);
|
||||
void leaveBlock();
|
||||
void emitEval(uint32 argc);
|
||||
void jsop_arguments();
|
||||
void jsop_arguments(RejoinState rejoin);
|
||||
bool jsop_tableswitch(jsbytecode *pc);
|
||||
void jsop_forprop(JSAtom *atom);
|
||||
void jsop_forname(JSAtom *atom);
|
||||
@ -707,6 +707,7 @@ class Compiler : public BaseCompiler
|
||||
bool jsop_setelem(bool popGuaranteed);
|
||||
bool jsop_getelem(bool isCall);
|
||||
void jsop_getelem_dense(bool isPacked);
|
||||
void jsop_getelem_args();
|
||||
bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
|
||||
void jsop_stricteq(JSOp op);
|
||||
bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
|
||||
|
@ -1110,7 +1110,7 @@ mjit::Compiler::jsop_setelem_dense()
|
||||
loop->hoistArrayLengthCheck(objv, indexv);
|
||||
|
||||
if (hoisted) {
|
||||
FrameEntry *slotsFe = loop->invariantSlots(objv);
|
||||
FrameEntry *slotsFe = loop->invariantArraySlots(objv);
|
||||
slotsReg = frame.tempRegForData(slotsFe);
|
||||
|
||||
frame.unpinEntry(vr);
|
||||
@ -1375,8 +1375,6 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed)
|
||||
static inline bool
|
||||
IsCacheableGetElem(FrameEntry *obj, FrameEntry *id)
|
||||
{
|
||||
if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT)
|
||||
return false;
|
||||
if (id->isTypeKnown() &&
|
||||
!(id->getKnownType() == JSVAL_TYPE_INT32
|
||||
#if defined JS_POLYIC
|
||||
@ -1435,7 +1433,7 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
|
||||
// we are hoisting the bounds check.
|
||||
RegisterID baseReg;
|
||||
if (hoisted) {
|
||||
FrameEntry *slotsFe = loop->invariantSlots(objv);
|
||||
FrameEntry *slotsFe = loop->invariantArraySlots(objv);
|
||||
baseReg = frame.tempRegForData(slotsFe);
|
||||
} else {
|
||||
baseReg = frame.tempRegForData(obj);
|
||||
@ -1521,6 +1519,78 @@ mjit::Compiler::jsop_getelem_dense(bool isPacked)
|
||||
finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_getelem_args()
|
||||
{
|
||||
FrameEntry *id = frame.peek(-1);
|
||||
|
||||
// Test for integer index.
|
||||
if (!id->isTypeKnown()) {
|
||||
Jump guard = frame.testInt32(Assembler::NotEqual, id);
|
||||
stubcc.linkExit(guard, Uses(2));
|
||||
}
|
||||
|
||||
// Allocate registers.
|
||||
|
||||
analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
|
||||
bool hoistedLength = loop && id->isType(JSVAL_TYPE_INT32) &&
|
||||
loop->hoistArgsLengthCheck(indexv);
|
||||
FrameEntry *actualsFe = loop ? loop->invariantArguments() : NULL;
|
||||
|
||||
Int32Key key = id->isConstant()
|
||||
? Int32Key::FromConstant(id->getValue().toInt32())
|
||||
: Int32Key::FromRegister(frame.tempRegForData(id));
|
||||
if (!key.isConstant())
|
||||
frame.pinReg(key.reg());
|
||||
|
||||
RegisterID dataReg = frame.allocReg();
|
||||
RegisterID typeReg = frame.allocReg();
|
||||
|
||||
if (!key.isConstant())
|
||||
frame.unpinReg(key.reg());
|
||||
|
||||
// Guard on nactual.
|
||||
if (!hoistedLength) {
|
||||
Address nactualAddr(JSFrameReg, StackFrame::offsetOfArgs());
|
||||
MaybeJump rangeGuard;
|
||||
if (key.isConstant()) {
|
||||
JS_ASSERT(key.index() >= 0);
|
||||
rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, Imm32(key.index()));
|
||||
} else {
|
||||
rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, key.reg());
|
||||
}
|
||||
stubcc.linkExit(rangeGuard.get(), Uses(2));
|
||||
}
|
||||
|
||||
RegisterID actualsReg;
|
||||
if (actualsFe) {
|
||||
actualsReg = frame.tempRegForData(actualsFe);
|
||||
} else {
|
||||
actualsReg = dataReg;
|
||||
masm.loadFrameActuals(outerScript->fun, actualsReg);
|
||||
}
|
||||
|
||||
if (key.isConstant()) {
|
||||
Address arg(actualsReg, key.index() * sizeof(Value));
|
||||
masm.loadValueAsComponents(arg, typeReg, dataReg);
|
||||
} else {
|
||||
JS_ASSERT(key.reg() != dataReg);
|
||||
BaseIndex arg(actualsReg, key.reg(), masm.JSVAL_SCALE);
|
||||
masm.loadValueAsComponents(arg, typeReg, dataReg);
|
||||
}
|
||||
|
||||
stubcc.leave();
|
||||
OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
|
||||
|
||||
frame.popn(2);
|
||||
frame.pushRegs(typeReg, dataReg, knownPushedType(0));
|
||||
BarrierState barrier = testBarrier(typeReg, dataReg, false);
|
||||
|
||||
stubcc.rejoin(Changes(2));
|
||||
|
||||
finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_getelem(bool isCall)
|
||||
{
|
||||
@ -1535,11 +1605,13 @@ mjit::Compiler::jsop_getelem(bool isCall)
|
||||
return true;
|
||||
}
|
||||
|
||||
frame.forgetMismatchedObject(obj);
|
||||
|
||||
if (cx->typeInferenceEnabled()) {
|
||||
if (cx->typeInferenceEnabled() && id->mightBeType(JSVAL_TYPE_INT32) && !isCall) {
|
||||
types::TypeSet *types = analysis->poppedTypes(PC, 1);
|
||||
if (!isCall && id->mightBeType(JSVAL_TYPE_INT32) &&
|
||||
if (types->isLazyArguments(cx) && !outerScript->analysis(cx)->modifiesArguments()) {
|
||||
jsop_getelem_args();
|
||||
return true;
|
||||
}
|
||||
if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
|
||||
!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
|
||||
!arrayPrototypeHasIndexedProperty()) {
|
||||
// this is definitely a dense array, generate code directly without
|
||||
@ -1550,6 +1622,8 @@ mjit::Compiler::jsop_getelem(bool isCall)
|
||||
}
|
||||
}
|
||||
|
||||
frame.forgetMismatchedObject(obj);
|
||||
|
||||
GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
|
||||
|
||||
// Pin the top of the stack to avoid spills, before allocating registers.
|
||||
|
@ -506,8 +506,8 @@ bool
|
||||
LoopState::hoistArrayLengthCheck(const CrossSSAValue &obj, const CrossSSAValue &index)
|
||||
{
|
||||
/*
|
||||
* Note: this method requires that obj is either a dense array or not an
|
||||
* object, and that the index is definitely an integer.
|
||||
* Note: this method requires that the index is definitely an integer, and
|
||||
* that obj is either a dense array or not an object.
|
||||
*/
|
||||
if (skipAnalysis)
|
||||
return false;
|
||||
@ -628,6 +628,58 @@ LoopState::hoistArrayLengthCheck(const CrossSSAValue &obj, const CrossSSAValue &
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
|
||||
{
|
||||
if (skipAnalysis)
|
||||
return false;
|
||||
|
||||
JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
|
||||
|
||||
uint32 indexSlot;
|
||||
int32 indexConstant;
|
||||
if (!getEntryValue(index, &indexSlot, &indexConstant)) {
|
||||
JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only hoist arguments checks which can be completely eliminated, for
|
||||
* now just tests with 'i < arguments.length' or similar in the condition.
|
||||
*/
|
||||
|
||||
if (indexSlot == UNASSIGNED || loopInvariantEntry(indexSlot)) {
|
||||
JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
|
||||
JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (indexSlot == testLHS && indexConstant == 0 && testConstant == -1 && testLessEqual) {
|
||||
bool found = false;
|
||||
for (unsigned i = 0; i < invariantEntries.length(); i++) {
|
||||
const InvariantEntry &entry = invariantEntries[i];
|
||||
if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) {
|
||||
uint32 slot = frame.outerSlot(frame.getTemporary(entry.u.array.temporary));
|
||||
if (slot == testRHS)
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
addNegativeCheck(indexSlot, indexConstant);
|
||||
JaegerSpew(JSpew_Analysis, "Access implied by loop test\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
JaegerSpew(JSpew_Analysis, "No match found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LoopState::hasTestLinearRelationship(uint32 slot)
|
||||
{
|
||||
@ -667,8 +719,10 @@ LoopState::hasTestLinearRelationship(uint32 slot)
|
||||
}
|
||||
|
||||
FrameEntry *
|
||||
LoopState::invariantSlots(const CrossSSAValue &obj)
|
||||
LoopState::invariantArraySlots(const CrossSSAValue &obj)
|
||||
{
|
||||
JS_ASSERT(!skipAnalysis);
|
||||
|
||||
uint32 objSlot;
|
||||
int32 objConstant;
|
||||
if (!getEntryValue(obj, &objSlot, &objConstant) || objConstant != 0) {
|
||||
@ -689,6 +743,33 @@ LoopState::invariantSlots(const CrossSSAValue &obj)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FrameEntry *
|
||||
LoopState::invariantArguments()
|
||||
{
|
||||
if (skipAnalysis)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < invariantEntries.length(); i++) {
|
||||
InvariantEntry &entry = invariantEntries[i];
|
||||
if (entry.kind == InvariantEntry::INVARIANT_ARGS_BASE)
|
||||
return frame.getTemporary(entry.u.array.temporary);
|
||||
}
|
||||
|
||||
uint32 which = frame.allocTemporary();
|
||||
if (which == uint32(-1))
|
||||
return NULL;
|
||||
FrameEntry *fe = frame.getTemporary(which);
|
||||
|
||||
InvariantEntry entry;
|
||||
entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
|
||||
entry.u.array.temporary = which;
|
||||
invariantEntries.append(entry);
|
||||
|
||||
JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
|
||||
frame.entryName(fe));
|
||||
return fe;
|
||||
}
|
||||
|
||||
FrameEntry *
|
||||
LoopState::invariantLength(const CrossSSAValue &obj)
|
||||
{
|
||||
@ -699,6 +780,32 @@ LoopState::invariantLength(const CrossSSAValue &obj)
|
||||
int32 objConstant;
|
||||
if (!getEntryValue(obj, &objSlot, &objConstant) || objConstant != 0)
|
||||
return NULL;
|
||||
TypeSet *objTypes = ssa->getValueTypes(obj);
|
||||
|
||||
/* Check for 'length' on the lazy arguments for the current frame. */
|
||||
if (objTypes->isLazyArguments(cx)) {
|
||||
JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME);
|
||||
|
||||
for (unsigned i = 0; i < invariantEntries.length(); i++) {
|
||||
InvariantEntry &entry = invariantEntries[i];
|
||||
if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH)
|
||||
return frame.getTemporary(entry.u.array.temporary);
|
||||
}
|
||||
|
||||
uint32 which = frame.allocTemporary();
|
||||
if (which == uint32(-1))
|
||||
return NULL;
|
||||
FrameEntry *fe = frame.getTemporary(which);
|
||||
|
||||
InvariantEntry entry;
|
||||
entry.kind = InvariantEntry::INVARIANT_ARGS_LENGTH;
|
||||
entry.u.array.temporary = which;
|
||||
invariantEntries.append(entry);
|
||||
|
||||
JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n",
|
||||
frame.entryName(fe));
|
||||
return fe;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < invariantEntries.length(); i++) {
|
||||
InvariantEntry &entry = invariantEntries[i];
|
||||
@ -711,7 +818,6 @@ LoopState::invariantLength(const CrossSSAValue &obj)
|
||||
if (!loopInvariantEntry(objSlot))
|
||||
return NULL;
|
||||
|
||||
TypeSet *objTypes = ssa->getValueTypes(obj);
|
||||
if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
|
||||
return NULL;
|
||||
|
||||
@ -1240,6 +1346,20 @@ LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm,
|
||||
break;
|
||||
}
|
||||
|
||||
case InvariantEntry::INVARIANT_ARGS_BASE: {
|
||||
Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
|
||||
masm.loadFrameActuals(outerScript->fun, T0);
|
||||
masm.storePtr(T0, address);
|
||||
break;
|
||||
}
|
||||
|
||||
case InvariantEntry::INVARIANT_ARGS_LENGTH: {
|
||||
Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
|
||||
masm.load32(Address(JSFrameReg, StackFrame::offsetOfArgs()), T0);
|
||||
masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
|
||||
break;
|
||||
}
|
||||
|
||||
case InvariantEntry::INVARIANT_PROPERTY: {
|
||||
uint32 object = entry.u.property.objectSlot;
|
||||
Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(object));
|
||||
|
@ -172,9 +172,15 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
/* constant >= value1 + value2 */
|
||||
RANGE_CHECK,
|
||||
|
||||
/* For dense arrays */
|
||||
INVARIANT_SLOTS,
|
||||
INVARIANT_LENGTH,
|
||||
|
||||
/* For lazy arguments */
|
||||
INVARIANT_ARGS_BASE,
|
||||
INVARIANT_ARGS_LENGTH,
|
||||
|
||||
/* For definite properties */
|
||||
INVARIANT_PROPERTY
|
||||
} kind;
|
||||
union {
|
||||
@ -276,7 +282,12 @@ class LoopState : public MacroAssemblerTypedefs
|
||||
*/
|
||||
bool hoistArrayLengthCheck(const analyze::CrossSSAValue &obj,
|
||||
const analyze::CrossSSAValue &index);
|
||||
FrameEntry *invariantSlots(const analyze::CrossSSAValue &obj);
|
||||
FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj);
|
||||
|
||||
/* Methods for accesses on lazy arguments. */
|
||||
bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index);
|
||||
FrameEntry *invariantArguments();
|
||||
|
||||
FrameEntry *invariantLength(const analyze::CrossSSAValue &obj);
|
||||
FrameEntry *invariantProperty(const analyze::CrossSSAValue &obj, jsid id);
|
||||
|
||||
|
@ -874,6 +874,8 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi
|
||||
FrameRegs &oldRegs = cx->regs();
|
||||
|
||||
fp->scopeChain();
|
||||
if (fp->isFunctionFrame() && fp->script()->usesArguments)
|
||||
fp->ensureCoherentArgCount();
|
||||
|
||||
JSBool ok;
|
||||
{
|
||||
|
@ -1723,6 +1723,10 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
||||
f.regs.sp[-1].setInt32(str->length());
|
||||
f.script()->typeMonitor(f.cx, f.pc(), f.regs.sp[-1]);
|
||||
return;
|
||||
} else if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
|
||||
f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
|
||||
f.script()->typeMonitor(f.cx, f.pc(), f.regs.sp[-1]);
|
||||
return;
|
||||
} else if (!f.regs.sp[-1].isPrimitive()) {
|
||||
JSObject *obj = &f.regs.sp[-1].toObject();
|
||||
if (obj->isArray() ||
|
||||
@ -2519,7 +2523,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
|
||||
// Right now, we don't optimize for strings.
|
||||
// Right now, we don't optimize for strings or lazy arguments.
|
||||
if (!f.regs.sp[-2].isObject()) {
|
||||
ic->disable(cx, "non-object");
|
||||
stubs::GetElem(f);
|
||||
|
@ -232,9 +232,6 @@ Recompiler::expandInlineFrames(JSContext *cx, StackFrame *fp, mjit::CallSite *in
|
||||
*/
|
||||
cx->compartment->types.frameExpansions++;
|
||||
|
||||
RejoinState rejoin = (RejoinState) f->stubRejoin;
|
||||
JS_ASSERT(rejoin != REJOIN_NATIVE && rejoin != REJOIN_NATIVE_LOWERED);
|
||||
|
||||
/*
|
||||
* Patch the VMFrame's return address if it is returning at the given inline site.
|
||||
* Note there is no worry about handling a native or CompileFunction call here,
|
||||
@ -249,13 +246,12 @@ Recompiler::expandInlineFrames(JSContext *cx, StackFrame *fp, mjit::CallSite *in
|
||||
StackFrame *innerfp = expandInlineFrameChain(cx, fp, inner);
|
||||
|
||||
/* Check if the VMFrame returns into the inlined frame. */
|
||||
if (f->stubRejoin) {
|
||||
if (f->stubRejoin && (f->stubRejoin & 0x1) && f->regs.fp()->prev() == fp) {
|
||||
/* The VMFrame is calling CompileFunction. */
|
||||
if (f->regs.fp()->prev() == fp) {
|
||||
fp->prev()->setRejoin(StubRejoin(rejoin));
|
||||
*frameAddr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
|
||||
}
|
||||
} else if (*frameAddr == codeStart + inlined->codeOffset) {
|
||||
fp->prev()->setRejoin(StubRejoin((RejoinState) f->stubRejoin));
|
||||
*frameAddr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
|
||||
}
|
||||
if (*frameAddr == codeStart + inlined->codeOffset) {
|
||||
/* The VMFrame returns directly into the expanded frame. */
|
||||
SetRejoinState(innerfp, *inlined, frameAddr);
|
||||
}
|
||||
|
@ -443,6 +443,17 @@ stubs::GetElem(VMFrame &f)
|
||||
}
|
||||
}
|
||||
|
||||
if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
|
||||
if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
|
||||
regs.sp[-2] = regs.fp()->canonicalActualArg(rref.toInt32());
|
||||
f.script()->typeMonitor(cx, f.pc(), regs.sp[-2]);
|
||||
return;
|
||||
}
|
||||
cx->markTypeObjectFlags(f.script()->fun->getType(),
|
||||
types::OBJECT_FLAG_CREATED_ARGUMENTS);
|
||||
JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
|
||||
}
|
||||
|
||||
JSObject *obj = ValueToObject(cx, &lref);
|
||||
if (!obj)
|
||||
THROW();
|
||||
@ -1965,6 +1976,14 @@ InlineGetProp(VMFrame &f)
|
||||
FrameRegs ®s = f.regs;
|
||||
|
||||
Value *vp = &f.regs.sp[-1];
|
||||
|
||||
if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
|
||||
JS_ASSERT(js_GetOpcode(cx, f.script(), f.pc()) == JSOP_LENGTH);
|
||||
regs.sp[-1] = Int32Value(regs.fp()->numActualArgs());
|
||||
f.script()->typeMonitor(cx, f.pc(), regs.sp[-1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *obj = ValueToObject(f.cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
@ -2826,14 +2845,14 @@ stubs::AssertArgumentTypes(VMFrame &f)
|
||||
JSScript *script = fun->script();
|
||||
|
||||
types::jstype type = types::GetValueType(f.cx, fp->thisValue());
|
||||
if (!script->thisTypes()->hasType(type)) {
|
||||
if (!types::TypeMatches(f.cx, script->thisTypes(), type)) {
|
||||
types::TypeFailure(f.cx, "Missing type for #%u this: %s", script->id(),
|
||||
types::TypeString(type));
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < fun->nargs; i++) {
|
||||
type = types::GetValueType(f.cx, fp->formalArg(i));
|
||||
if (!script->argTypes(i)->hasType(type)) {
|
||||
if (!types::TypeMatches(f.cx, script->argTypes(i), type)) {
|
||||
types::TypeFailure(f.cx, "Missing type for #%u arg %d: %s", script->id(), i,
|
||||
types::TypeString(type));
|
||||
}
|
||||
|
@ -496,10 +496,6 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
|
||||
PodCopy(vp, othervp, othersp - othervp);
|
||||
JS_ASSERT(vp == this->actualArgs() - 2);
|
||||
|
||||
/* Catch bad-touching of non-canonical args (e.g., generator_trace). */
|
||||
if (otherfp->hasOverflowArgs())
|
||||
Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
|
||||
|
||||
/*
|
||||
* Repoint Call, Arguments, Block and With objects to the new live frame.
|
||||
* Call and Arguments are done directly because we have pointers to them.
|
||||
@ -606,6 +602,13 @@ StackFrame::numActualArgs() const
|
||||
return numFormalArgs();
|
||||
}
|
||||
|
||||
inline void
|
||||
StackFrame::ensureCoherentArgCount()
|
||||
{
|
||||
if (!hasArgsObj())
|
||||
args.nactual = numActualArgs();
|
||||
}
|
||||
|
||||
inline Value *
|
||||
StackFrame::actualArgs() const
|
||||
{
|
||||
@ -903,7 +906,6 @@ ContextStack::getCallFrame(JSContext *cx, Value *firstUnused, uintN nactual,
|
||||
Value *dst = firstUnused;
|
||||
Value *src = firstUnused - (2 + nactual);
|
||||
PodCopy(dst, src, ncopy);
|
||||
Debug_SetValueRangeToCrashOnTouch(src, ncopy);
|
||||
return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
|
||||
}
|
||||
|
||||
|
@ -577,6 +577,7 @@ class StackFrame
|
||||
inline uintN numActualArgs() const;
|
||||
inline js::Value *actualArgs() const;
|
||||
inline js::Value *actualArgsEnd() const;
|
||||
inline void ensureCoherentArgCount();
|
||||
|
||||
inline js::Value &canonicalActualArg(uintN i) const;
|
||||
template <class Op> inline bool forEachCanonicalActualArg(Op op);
|
||||
@ -958,6 +959,10 @@ class StackFrame
|
||||
return &args;
|
||||
}
|
||||
|
||||
static size_t offsetOfArgs() {
|
||||
return offsetof(StackFrame, args);
|
||||
}
|
||||
|
||||
static size_t offsetOfScopeChain() {
|
||||
return offsetof(StackFrame, scopeChain_);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user