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
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user