[INFER] Ensure that inlined scripts always have JIT code for expanding, bug 645666.

This commit is contained in:
Brian Hackett 2011-03-29 08:30:05 -07:00
parent aa6b5e0e2f
commit 1f1a4d216c
6 changed files with 131 additions and 47 deletions

View File

@ -0,0 +1,16 @@
function f1() {
gc();
}
function f2() {
with(this) {};
f1();
}
function f3() {
f2();
}
function f4() {
f3();
}
f3();
f3();
f4();

View File

@ -420,6 +420,7 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
}
#if defined JS_METHODJIT && defined JS_MONOIC
/*
* Check if the pool containing the code for jit should be destroyed, per the
* heuristics in JSCompartment::sweep.
@ -444,7 +445,34 @@ ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
}
return pool->m_destroy;
}
#endif
static inline void
ScriptTryDestroyCode(JSContext *cx, JSScript *script, mjit::JITScript *jit,
uint32 releaseInterval, uint32 &counter)
{
/*
* Check if the JIT code for script should be destroyed. When JIT code has
* inlined frames, destroy the outer script if any of the inner scripts
* will need to be destroyed, preserving the invariant that we always have
* JIT code for any inlined frame which may need to be expanded.
*/
if (ScriptPoolDestroyed(cx, jit, releaseInterval, counter)) {
mjit::ReleaseScriptCode(cx, script);
return;
}
for (unsigned i = 0; i < jit->nInlineFrames; i++) {
JSScript *inner = jit->inlineFrames()[i].fun->script();
JS_ASSERT(inner->jitNormal);
if (ScriptPoolDestroyed(cx, inner->jitNormal, releaseInterval, counter)) {
mjit::ReleaseScriptCode(cx, script);
return;
}
}
}
#endif // JS_METHODJIT && JS_MONOIC
/*
* This method marks pointers that cross compartment boundaries. It should be
@ -541,15 +569,10 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
if (script->hasJITCode()) {
mjit::ic::SweepCallICs(cx, script, discardScripts);
if (discardScripts) {
if (script->jitNormal &&
ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) {
mjit::ReleaseScriptCode(cx, script);
continue;
}
if (script->jitCtor &&
ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
mjit::ReleaseScriptCode(cx, script);
}
if (script->jitNormal)
ScriptTryDestroyCode(cx, script, script->jitNormal, releaseInterval, counter);
if (script->jitCtor)
ScriptTryDestroyCode(cx, script, script->jitCtor, releaseInterval, counter);
}
}
}

View File

@ -463,6 +463,7 @@ struct JSScript {
#ifdef JS_METHODJIT
bool debugMode:1; /* script was compiled in debug mode */
bool singleStepMode:1; /* compile script in single-step mode */
bool inlineParents:1; /* script may be inlined in other frames */
#endif
jsbytecode *main; /* main entry point, after predef'ing prolog */

View File

@ -89,14 +89,15 @@ static const char *OpcodeNames[] = {
*/
static const size_t CALLS_BACKEDGES_BEFORE_INLINING = 10000;
mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp,
mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
bool isConstructing, bool isEval, JSObject *globalObj,
const Vector<PatchableFrame> *patchFrames, bool recompiling)
: BaseCompiler(cx),
fp(fp), outerScript(fp->script()),
outerScript(outerScript),
isConstructing(isConstructing),
isEval(isEval),
globalObj(globalObj),
patchFrames(patchFrames),
scopeChain(&fp->scopeChain()),
globalObj(scopeChain->getGlobal()),
isConstructing(fp->isConstructing()),
savedTraps(NULL),
frame(cx, *this, masm, stubcc),
a(NULL), outer(NULL), script(NULL), PC(NULL),
@ -283,8 +284,6 @@ mjit::Compiler::popActiveFrame()
CompileStatus
mjit::Compiler::performCompilation(JITScript **jitp)
{
outerScript = fp->script();
JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
outerScript->filename, outerScript->lineno, outerScript->length);
@ -324,6 +323,39 @@ mjit::Compiler::performCompilation(JITScript **jitp)
if (!*jitp)
return Compile_Abort;
/*
* Make sure any inlined scripts have JIT code associated that we can
* rejoin into if we expand the inlined frames.
*/
for (unsigned i = 0; i < (*jitp)->nInlineFrames; i++) {
JSScript *script = (*jitp)->inlineFrames()[i].fun->script();
script->inlineParents = true;
/* We should have bailed out while inlining if the script is unjittable. */
JS_ASSERT(script->jitArityCheckNormal != JS_UNJITTABLE_SCRIPT);
if (script->jitNormal && !script->jitNormal->rejoinPoints) {
mjit::Recompiler recompiler(cx, script);
if (!recompiler.recompile()) {
ReleaseScriptCode(cx, outerScript);
return Compile_Error;
}
}
if (!script->jitNormal) {
CompileStatus status = Compile_Retry;
while (status == Compile_Retry) {
mjit::Compiler cc(cx, script, isConstructing, false, globalObj, NULL, true);
status = cc.compile();
}
if (status != Compile_Okay) {
ReleaseScriptCode(cx, outerScript);
return status;
}
}
}
return Compile_Okay;
}
@ -417,7 +449,8 @@ mjit::TryCompile(JSContext *cx, JSStackFrame *fp)
// before giving up.
CompileStatus status = Compile_Retry;
for (unsigned i = 0; status == Compile_Retry && i < 5; i++) {
Compiler cc(cx, fp, NULL, false);
Compiler cc(cx, fp->script(), fp->isConstructing(), fp->isEvalFrame(),
fp->scopeChain().getGlobal(), NULL, fp->script()->inlineParents);
status = cc.compile();
}
@ -701,6 +734,12 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
JS_ASSERT(outerScript == script);
/*
* We always need to remit rejoin points when compiling a script with inline parents,
* so we can expand inline frames at any point.
*/
JS_ASSERT_IF(outerScript->inlineParents, recompiling);
jit->script = script;
jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
jit->invokeEntry = result;
@ -2902,7 +2941,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
emitFinalReturn(stubcc.masm);
}
} else {
if (fp->isEvalFrame() && script->strictModeCode) {
if (isEval && script->strictModeCode) {
/* There will always be a call object. */
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
@ -3575,6 +3614,7 @@ mjit::Compiler::inlineScriptedFunction(uint32 argc, bool callingNew)
JSFunction *fun = object->singleton->getFunctionPrivate();
if (!fun->isInterpreted())
return Compile_InlineAbort;
JSScript *script = fun->script();
/*
* The outer and inner scripts must have the same scope. This only
@ -3583,22 +3623,22 @@ mjit::Compiler::inlineScriptedFunction(uint32 argc, bool callingNew)
*/
if (!outerScript->compileAndGo ||
(outerScript->fun && outerScript->fun->getParent() != globalObj) ||
!fun->script()->compileAndGo ||
!script->compileAndGo ||
fun->getParent() != globalObj ||
outerScript->strictModeCode != fun->script()->strictModeCode) {
outerScript->strictModeCode != script->strictModeCode) {
return Compile_InlineAbort;
}
/* We can't cope with inlining recursive functions yet. */
ActiveFrame *checka = a;
while (checka) {
if (checka->script == fun->script())
if (checka->script == script)
return Compile_InlineAbort;
checka = checka->parent;
}
analyze::Script analysis;
analysis.analyze(cx, fun->script());
analysis.analyze(cx, script);
if (analysis.OOM())
return Compile_Error;
@ -4213,7 +4253,7 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
* the global object for the script we are now compiling.
*/
JSObject *obj;
if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
if (!js_GetClassPrototype(cx, globalObj, JSProto_String, &obj))
return false;
/* Force into a register because getprop won't expect a constant. */

View File

@ -351,15 +351,15 @@ class Compiler : public BaseCompiler
Label label;
};
JSStackFrame *fp;
JSScript *outerScript;
bool isConstructing;
bool isEval;
JSObject *globalObj;
/* Existing frames on the stack whose slots may need to be updated. */
const Vector<PatchableFrame> *patchFrames;
JSObject *scopeChain;
JSObject *globalObj;
bool isConstructing;
bool *savedTraps;
Assembler masm;
FrameState frame;
@ -452,7 +452,9 @@ class Compiler : public BaseCompiler
// follows interpreter usage in JSOP_LENGTH.
enum { LengthAtomIndex = uint32(-2) };
Compiler(JSContext *cx, JSStackFrame *fp, const Vector<PatchableFrame> *patchFrames, bool recompiling);
Compiler(JSContext *cx, JSScript *outerScript, bool isConstructing, bool isEval,
JSObject *globalObj,
const Vector<PatchableFrame> *patchFrames, bool recompiling);
~Compiler();
CompileStatus compile();

View File

@ -252,24 +252,14 @@ Recompiler::expandInlineFrameChain(JSContext *cx, JSStackFrame *outer, InlineFra
uint32 pcOffset = inner->parentpc - parent->script()->code;
/*
* The erased frame needs JIT code with rejoin points added. Note that the
* outer frame does not need to have rejoin points, as it is definitely at
* an inline call and rejoin points are always added for such calls.
* We should have ensured during compilation that the erased frame has JIT
* code with rejoin points added. We don't try to compile such code on
* demand as this can trigger recompilations and a reentrant invocation of
* expandInlineFrames. Note that the outer frame does not need to have
* rejoin points, as it is definitely at an inline call and rejoin points
* are always added for such calls.
*/
if (fp->jit() && !fp->jit()->rejoinPoints) {
mjit::Recompiler recompiler(cx, fp->script());
if (!recompiler.recompile())
return NULL; // FIXME
}
if (!fp->jit()) {
CompileStatus status = Compile_Retry;
while (status == Compile_Retry) {
mjit::Compiler cc(cx, fp, NULL, true);
status = cc.compile();
}
if (status != Compile_Okay)
return NULL; // FIXME
}
JS_ASSERT(fp->jit() && fp->jit()->rejoinPoints);
PatchableAddress patch;
patch.location = fp->addressOfNativeReturnAddress();
@ -522,6 +512,17 @@ Recompiler::recompile()
return false;
}
/* Make sure that scripts with inline parents still have JIT code. */
if (script->inlineParents && !script->jitNormal) {
CompileStatus status = Compile_Retry;
while (status == Compile_Retry) {
mjit::Compiler cc(cx, script, false, false, script->global, NULL, true);
status = cc.compile();
}
if (status != Compile_Okay)
return false;
}
return true;
}
@ -566,7 +567,8 @@ Recompiler::recompile(Vector<PatchableFrame> &frames, Vector<PatchableAddress> &
CompileStatus status = Compile_Retry;
while (status == Compile_Retry) {
Compiler cc(cx, fp, &frames, true);
Compiler cc(cx, fp->script(), fp->isConstructing(), fp->isEvalFrame(),
fp->scopeChain().getGlobal(), &frames, true);
if (!cc.loadOldTraps(sites))
return false;
status = cc.compile();