mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Ensure that inlined scripts always have JIT code for expanding, bug 645666.
This commit is contained in:
parent
aa6b5e0e2f
commit
1f1a4d216c
16
js/src/jit-test/tests/jaeger/inline/bug645666.js
Normal file
16
js/src/jit-test/tests/jaeger/inline/bug645666.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
function f1() {
|
||||||
|
gc();
|
||||||
|
}
|
||||||
|
function f2() {
|
||||||
|
with(this) {};
|
||||||
|
f1();
|
||||||
|
}
|
||||||
|
function f3() {
|
||||||
|
f2();
|
||||||
|
}
|
||||||
|
function f4() {
|
||||||
|
f3();
|
||||||
|
}
|
||||||
|
f3();
|
||||||
|
f3();
|
||||||
|
f4();
|
@ -420,6 +420,7 @@ JSCompartment::wrap(JSContext *cx, AutoIdVector &props)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined JS_METHODJIT && defined JS_MONOIC
|
#if defined JS_METHODJIT && defined JS_MONOIC
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the pool containing the code for jit should be destroyed, per the
|
* Check if the pool containing the code for jit should be destroyed, per the
|
||||||
* heuristics in JSCompartment::sweep.
|
* heuristics in JSCompartment::sweep.
|
||||||
@ -444,7 +445,34 @@ ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
|
|||||||
}
|
}
|
||||||
return pool->m_destroy;
|
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
|
* This method marks pointers that cross compartment boundaries. It should be
|
||||||
@ -541,15 +569,10 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
|||||||
if (script->hasJITCode()) {
|
if (script->hasJITCode()) {
|
||||||
mjit::ic::SweepCallICs(cx, script, discardScripts);
|
mjit::ic::SweepCallICs(cx, script, discardScripts);
|
||||||
if (discardScripts) {
|
if (discardScripts) {
|
||||||
if (script->jitNormal &&
|
if (script->jitNormal)
|
||||||
ScriptPoolDestroyed(cx, script->jitNormal, releaseInterval, counter)) {
|
ScriptTryDestroyCode(cx, script, script->jitNormal, releaseInterval, counter);
|
||||||
mjit::ReleaseScriptCode(cx, script);
|
if (script->jitCtor)
|
||||||
continue;
|
ScriptTryDestroyCode(cx, script, script->jitCtor, releaseInterval, counter);
|
||||||
}
|
|
||||||
if (script->jitCtor &&
|
|
||||||
ScriptPoolDestroyed(cx, script->jitCtor, releaseInterval, counter)) {
|
|
||||||
mjit::ReleaseScriptCode(cx, script);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,6 +463,7 @@ struct JSScript {
|
|||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
bool debugMode:1; /* script was compiled in debug mode */
|
bool debugMode:1; /* script was compiled in debug mode */
|
||||||
bool singleStepMode:1; /* compile script in single-step mode */
|
bool singleStepMode:1; /* compile script in single-step mode */
|
||||||
|
bool inlineParents:1; /* script may be inlined in other frames */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
jsbytecode *main; /* main entry point, after predef'ing prolog */
|
jsbytecode *main; /* main entry point, after predef'ing prolog */
|
||||||
|
@ -89,14 +89,15 @@ static const char *OpcodeNames[] = {
|
|||||||
*/
|
*/
|
||||||
static const size_t CALLS_BACKEDGES_BEFORE_INLINING = 10000;
|
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)
|
const Vector<PatchableFrame> *patchFrames, bool recompiling)
|
||||||
: BaseCompiler(cx),
|
: BaseCompiler(cx),
|
||||||
fp(fp), outerScript(fp->script()),
|
outerScript(outerScript),
|
||||||
|
isConstructing(isConstructing),
|
||||||
|
isEval(isEval),
|
||||||
|
globalObj(globalObj),
|
||||||
patchFrames(patchFrames),
|
patchFrames(patchFrames),
|
||||||
scopeChain(&fp->scopeChain()),
|
|
||||||
globalObj(scopeChain->getGlobal()),
|
|
||||||
isConstructing(fp->isConstructing()),
|
|
||||||
savedTraps(NULL),
|
savedTraps(NULL),
|
||||||
frame(cx, *this, masm, stubcc),
|
frame(cx, *this, masm, stubcc),
|
||||||
a(NULL), outer(NULL), script(NULL), PC(NULL),
|
a(NULL), outer(NULL), script(NULL), PC(NULL),
|
||||||
@ -283,8 +284,6 @@ mjit::Compiler::popActiveFrame()
|
|||||||
CompileStatus
|
CompileStatus
|
||||||
mjit::Compiler::performCompilation(JITScript **jitp)
|
mjit::Compiler::performCompilation(JITScript **jitp)
|
||||||
{
|
{
|
||||||
outerScript = fp->script();
|
|
||||||
|
|
||||||
JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
|
JaegerSpew(JSpew_Scripts, "compiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
|
||||||
outerScript->filename, outerScript->lineno, outerScript->length);
|
outerScript->filename, outerScript->lineno, outerScript->length);
|
||||||
|
|
||||||
@ -324,6 +323,39 @@ mjit::Compiler::performCompilation(JITScript **jitp)
|
|||||||
if (!*jitp)
|
if (!*jitp)
|
||||||
return Compile_Abort;
|
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;
|
return Compile_Okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +449,8 @@ mjit::TryCompile(JSContext *cx, JSStackFrame *fp)
|
|||||||
// before giving up.
|
// before giving up.
|
||||||
CompileStatus status = Compile_Retry;
|
CompileStatus status = Compile_Retry;
|
||||||
for (unsigned i = 0; status == Compile_Retry && i < 5; i++) {
|
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();
|
status = cc.compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +734,12 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
|||||||
|
|
||||||
JS_ASSERT(outerScript == script);
|
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->script = script;
|
||||||
jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
|
jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
|
||||||
jit->invokeEntry = result;
|
jit->invokeEntry = result;
|
||||||
@ -2902,7 +2941,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||||||
emitFinalReturn(stubcc.masm);
|
emitFinalReturn(stubcc.masm);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (fp->isEvalFrame() && script->strictModeCode) {
|
if (isEval && script->strictModeCode) {
|
||||||
/* There will always be a call object. */
|
/* There will always be a call object. */
|
||||||
prepareStubCall(Uses(fe ? 1 : 0));
|
prepareStubCall(Uses(fe ? 1 : 0));
|
||||||
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
|
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
|
||||||
@ -3575,6 +3614,7 @@ mjit::Compiler::inlineScriptedFunction(uint32 argc, bool callingNew)
|
|||||||
JSFunction *fun = object->singleton->getFunctionPrivate();
|
JSFunction *fun = object->singleton->getFunctionPrivate();
|
||||||
if (!fun->isInterpreted())
|
if (!fun->isInterpreted())
|
||||||
return Compile_InlineAbort;
|
return Compile_InlineAbort;
|
||||||
|
JSScript *script = fun->script();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The outer and inner scripts must have the same scope. This only
|
* 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 ||
|
if (!outerScript->compileAndGo ||
|
||||||
(outerScript->fun && outerScript->fun->getParent() != globalObj) ||
|
(outerScript->fun && outerScript->fun->getParent() != globalObj) ||
|
||||||
!fun->script()->compileAndGo ||
|
!script->compileAndGo ||
|
||||||
fun->getParent() != globalObj ||
|
fun->getParent() != globalObj ||
|
||||||
outerScript->strictModeCode != fun->script()->strictModeCode) {
|
outerScript->strictModeCode != script->strictModeCode) {
|
||||||
return Compile_InlineAbort;
|
return Compile_InlineAbort;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can't cope with inlining recursive functions yet. */
|
/* We can't cope with inlining recursive functions yet. */
|
||||||
ActiveFrame *checka = a;
|
ActiveFrame *checka = a;
|
||||||
while (checka) {
|
while (checka) {
|
||||||
if (checka->script == fun->script())
|
if (checka->script == script)
|
||||||
return Compile_InlineAbort;
|
return Compile_InlineAbort;
|
||||||
checka = checka->parent;
|
checka = checka->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
analyze::Script analysis;
|
analyze::Script analysis;
|
||||||
analysis.analyze(cx, fun->script());
|
analysis.analyze(cx, script);
|
||||||
|
|
||||||
if (analysis.OOM())
|
if (analysis.OOM())
|
||||||
return Compile_Error;
|
return Compile_Error;
|
||||||
@ -4213,7 +4253,7 @@ mjit::Compiler::jsop_callprop_str(JSAtom *atom)
|
|||||||
* the global object for the script we are now compiling.
|
* the global object for the script we are now compiling.
|
||||||
*/
|
*/
|
||||||
JSObject *obj;
|
JSObject *obj;
|
||||||
if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
|
if (!js_GetClassPrototype(cx, globalObj, JSProto_String, &obj))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Force into a register because getprop won't expect a constant. */
|
/* Force into a register because getprop won't expect a constant. */
|
||||||
|
@ -351,15 +351,15 @@ class Compiler : public BaseCompiler
|
|||||||
Label label;
|
Label label;
|
||||||
};
|
};
|
||||||
|
|
||||||
JSStackFrame *fp;
|
|
||||||
JSScript *outerScript;
|
JSScript *outerScript;
|
||||||
|
bool isConstructing;
|
||||||
|
bool isEval;
|
||||||
|
|
||||||
|
JSObject *globalObj;
|
||||||
|
|
||||||
/* Existing frames on the stack whose slots may need to be updated. */
|
/* Existing frames on the stack whose slots may need to be updated. */
|
||||||
const Vector<PatchableFrame> *patchFrames;
|
const Vector<PatchableFrame> *patchFrames;
|
||||||
|
|
||||||
JSObject *scopeChain;
|
|
||||||
JSObject *globalObj;
|
|
||||||
bool isConstructing;
|
|
||||||
bool *savedTraps;
|
bool *savedTraps;
|
||||||
Assembler masm;
|
Assembler masm;
|
||||||
FrameState frame;
|
FrameState frame;
|
||||||
@ -452,7 +452,9 @@ class Compiler : public BaseCompiler
|
|||||||
// follows interpreter usage in JSOP_LENGTH.
|
// follows interpreter usage in JSOP_LENGTH.
|
||||||
enum { LengthAtomIndex = uint32(-2) };
|
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();
|
~Compiler();
|
||||||
|
|
||||||
CompileStatus compile();
|
CompileStatus compile();
|
||||||
|
@ -252,24 +252,14 @@ Recompiler::expandInlineFrameChain(JSContext *cx, JSStackFrame *outer, InlineFra
|
|||||||
uint32 pcOffset = inner->parentpc - parent->script()->code;
|
uint32 pcOffset = inner->parentpc - parent->script()->code;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The erased frame needs JIT code with rejoin points added. Note that the
|
* We should have ensured during compilation that the erased frame has JIT
|
||||||
* outer frame does not need to have rejoin points, as it is definitely at
|
* code with rejoin points added. We don't try to compile such code on
|
||||||
* an inline call and rejoin points are always added for such calls.
|
* 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) {
|
JS_ASSERT(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
|
|
||||||
}
|
|
||||||
|
|
||||||
PatchableAddress patch;
|
PatchableAddress patch;
|
||||||
patch.location = fp->addressOfNativeReturnAddress();
|
patch.location = fp->addressOfNativeReturnAddress();
|
||||||
@ -522,6 +512,17 @@ Recompiler::recompile()
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,7 +567,8 @@ Recompiler::recompile(Vector<PatchableFrame> &frames, Vector<PatchableAddress> &
|
|||||||
|
|
||||||
CompileStatus status = Compile_Retry;
|
CompileStatus status = Compile_Retry;
|
||||||
while (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))
|
if (!cc.loadOldTraps(sites))
|
||||||
return false;
|
return false;
|
||||||
status = cc.compile();
|
status = cc.compile();
|
||||||
|
Loading…
Reference in New Issue
Block a user