mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Allow JaegerShot to only partially execute frames, bug 665815. r=luke
This commit is contained in:
parent
8453d326d9
commit
eb4a0fc5ea
15
js/src/jit-test/tests/basic/testApplyInterpretLowered.js
Normal file
15
js/src/jit-test/tests/basic/testApplyInterpretLowered.js
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
/* Read correct return value when the interpreter pops a lowered call/apply. */
|
||||
|
||||
function recompile() {}
|
||||
|
||||
function foo() {
|
||||
if (arguments[0] == 9)
|
||||
recompile();
|
||||
return arguments[0];
|
||||
}
|
||||
function bar() {
|
||||
for (var i = 0; i < 10; i++)
|
||||
assertEq(foo.apply(null, [i]), i);
|
||||
}
|
||||
bar();
|
12
js/src/jit-test/tests/basic/testApplyInterpretLowered2.js
Normal file
12
js/src/jit-test/tests/basic/testApplyInterpretLowered2.js
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
/* Make sure the interpreter can pop lowered frames pushed by CompileFunction. */
|
||||
|
||||
function recompile() {}
|
||||
|
||||
function bar() {
|
||||
for (var i = 0; i < 50; i++) {
|
||||
var foo = new Function("recompile(arguments[0] + " + i + "); return arguments[0]");
|
||||
assertEq(foo.apply(null, [i]), i);
|
||||
}
|
||||
}
|
||||
bar();
|
@ -507,7 +507,7 @@ js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, js::Native native,
|
||||
/*
|
||||
* Flags for js_ValueToFunction and js_ReportIsNotFunction.
|
||||
*/
|
||||
#define JSV2F_CONSTRUCT CONSTRUCT
|
||||
#define JSV2F_CONSTRUCT INITIAL_CONSTRUCT
|
||||
#define JSV2F_SEARCH_STACK 0x10000
|
||||
|
||||
extern JSFunction *
|
||||
|
@ -391,6 +391,14 @@ FixObjectType(JSContext *cx, JSObject *obj)
|
||||
extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
|
||||
extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::jstype type);
|
||||
|
||||
inline bool
|
||||
UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
return fp->isConstructing() && cx->typeInferenceEnabled() &&
|
||||
fp->prev() && fp->prev()->isScriptFrame() &&
|
||||
UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Script interface functions
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -615,7 +615,7 @@ RunScript(JSContext *cx, JSScript *script, StackFrame *fp)
|
||||
return false;
|
||||
|
||||
if (status == mjit::Compile_Okay)
|
||||
return mjit::JaegerShot(cx);
|
||||
return mjit::JaegerShot(cx, false);
|
||||
#endif
|
||||
|
||||
return Interpret(cx, fp);
|
||||
@ -637,8 +637,11 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
|
||||
|
||||
JS_ASSERT(!cx->compartment->activeAnalysis);
|
||||
|
||||
/* MaybeConstruct is a subset of InitialFrameFlags */
|
||||
InitialFrameFlags initial = (InitialFrameFlags) construct;
|
||||
|
||||
if (args.calleev().isPrimitive()) {
|
||||
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(construct));
|
||||
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -653,7 +656,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
|
||||
#endif
|
||||
JS_ASSERT_IF(construct, !clasp->construct);
|
||||
if (!clasp->call) {
|
||||
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(construct));
|
||||
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(initial));
|
||||
return false;
|
||||
}
|
||||
return CallJSNative(cx, clasp->call, args);
|
||||
@ -685,7 +688,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
|
||||
|
||||
/* Get pointer to new frame/slots, prepare arguments. */
|
||||
InvokeFrameGuard ifg;
|
||||
if (!cx->stack.pushInvokeFrame(cx, args, construct, &ifg))
|
||||
if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
|
||||
return false;
|
||||
|
||||
/* Now that the new frame is rooted, maybe create a call object. */
|
||||
@ -745,7 +748,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
|
||||
break;
|
||||
|
||||
/* Push the stack frame once for the session. */
|
||||
if (!stack.pushInvokeFrame(cx, args_, NO_CONSTRUCT, &ifg_))
|
||||
if (!stack.pushInvokeFrame(cx, args_, INITIAL_NONE, &ifg_))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -1868,7 +1871,10 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||
if (status == mjit::Compile_Okay) { \
|
||||
void *ncode = \
|
||||
script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);\
|
||||
interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
|
||||
mjit::JaegerStatus status = \
|
||||
mjit::JaegerShotAtSafePoint(cx, ncode, true); \
|
||||
CHECK_PARTIAL_METHODJIT(status); \
|
||||
interpReturnOK = (status == mjit::Jaeger_Returned); \
|
||||
if (entryFrame != regs.fp()) \
|
||||
goto jit_return; \
|
||||
regs.fp()->setFinishedInInterpreter(); \
|
||||
@ -1879,6 +1885,21 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define CHECK_PARTIAL_METHODJIT(status) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (status == mjit::Jaeger_Unfinished) { \
|
||||
op = (JSOp) *regs.pc; \
|
||||
RESTORE_INTERP_VARS(); \
|
||||
DO_OP(); \
|
||||
} else if (status == mjit::Jaeger_UnfinishedAtTrap) { \
|
||||
interpMode = JSINTERP_SKIP_TRAP; \
|
||||
JS_ASSERT(JSOp(*regs.pc) == JSOP_TRAP); \
|
||||
op = JSOP_TRAP; \
|
||||
RESTORE_INTERP_VARS(); \
|
||||
DO_OP(); \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#else
|
||||
|
||||
#define RESET_USE_METHODJIT() ((void) 0)
|
||||
@ -1909,6 +1930,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||
#define RESTORE_INTERP_VARS() \
|
||||
JS_BEGIN_MACRO \
|
||||
script = regs.fp()->script(); \
|
||||
pcCounts = script->pcCounters.get(JSRUNMODE_INTERP); \
|
||||
argv = regs.fp()->maybeFormalArgs(); \
|
||||
atoms = FrameAtomBase(cx, regs.fp()); \
|
||||
JS_ASSERT(&cx->regs() == ®s); \
|
||||
@ -2074,10 +2096,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
|
||||
if (interpMode == JSINTERP_NORMAL) {
|
||||
StackFrame *fp = regs.fp();
|
||||
JS_ASSERT_IF(!fp->isGeneratorFrame(), regs.pc == script->code);
|
||||
bool newType = fp->isConstructing() && cx->typeInferenceEnabled() &&
|
||||
fp->prev() && fp->prev()->isScriptFrame() &&
|
||||
UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
|
||||
if (!ScriptPrologueOrGeneratorResume(cx, fp, newType))
|
||||
if (!ScriptPrologueOrGeneratorResume(cx, fp, UseNewTypeAtEntry(cx, fp)))
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -2403,13 +2422,13 @@ BEGIN_CASE(JSOP_STOP)
|
||||
#ifdef JS_METHODJIT
|
||||
jit_return:
|
||||
#endif
|
||||
|
||||
/* The results of lowered call/apply frames need to be shifted. */
|
||||
bool shiftResult = regs.fp()->loweredCallOrApply();
|
||||
|
||||
cx->stack.popInlineFrame(regs);
|
||||
|
||||
/* Sync interpreter locals. */
|
||||
script = regs.fp()->script();
|
||||
pcCounts = script->pcCounters.get(JSRUNMODE_INTERP);
|
||||
argv = regs.fp()->maybeFormalArgs();
|
||||
atoms = FrameAtomBase(cx, regs.fp());
|
||||
RESTORE_INTERP_VARS();
|
||||
|
||||
/* Resume execution in the calling frame. */
|
||||
RESET_USE_METHODJIT();
|
||||
@ -2418,7 +2437,15 @@ BEGIN_CASE(JSOP_STOP)
|
||||
== JSOP_CALL_LENGTH);
|
||||
TRACE_0(LeaveFrame);
|
||||
script->types.monitor(cx, regs.pc, regs.sp[-1]);
|
||||
|
||||
op = JSOp(*regs.pc);
|
||||
len = JSOP_CALL_LENGTH;
|
||||
|
||||
if (shiftResult) {
|
||||
regs.sp[-2] = regs.sp[-1];
|
||||
regs.sp--;
|
||||
}
|
||||
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
goto error;
|
||||
@ -4158,7 +4185,7 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
||||
CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
|
||||
JS_ASSERT(args.base() >= regs.fp()->base());
|
||||
|
||||
MaybeConstruct construct = *regs.pc == JSOP_NEW ? CONSTRUCT : NO_CONSTRUCT;
|
||||
bool construct = (*regs.pc == JSOP_NEW);
|
||||
|
||||
JSObject *callee;
|
||||
JSFunction *fun;
|
||||
@ -4182,15 +4209,13 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
||||
|
||||
TypeMonitorCall(cx, args, construct);
|
||||
|
||||
InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
|
||||
|
||||
JSScript *newScript = fun->script();
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, construct, OOMCheck()))
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, initial, OOMCheck()))
|
||||
goto error;
|
||||
|
||||
/* Refresh local js::Interpret state. */
|
||||
script = newScript;
|
||||
pcCounts = script->pcCounters.get(JSRUNMODE_INTERP);
|
||||
argv = regs.fp()->formalArgsEnd() - fun->nargs;
|
||||
atoms = script->atomMap.vector;
|
||||
RESTORE_INTERP_VARS();
|
||||
|
||||
/* Only create call object after frame is rooted. */
|
||||
if (fun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp()))
|
||||
@ -4209,7 +4234,9 @@ BEGIN_CASE(JSOP_FUNAPPLY)
|
||||
if (status == mjit::Compile_Error)
|
||||
goto error;
|
||||
if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
|
||||
interpReturnOK = mjit::JaegerShot(cx);
|
||||
mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
|
||||
CHECK_PARTIAL_METHODJIT(status);
|
||||
interpReturnOK = (status == mjit::Jaeger_Returned);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
goto jit_return;
|
||||
}
|
||||
|
@ -144,6 +144,11 @@ BoxNonStrictThis(JSContext *cx, const CallReceiver &call);
|
||||
inline bool
|
||||
ComputeThis(JSContext *cx, StackFrame *fp);
|
||||
|
||||
enum MaybeConstruct {
|
||||
NO_CONSTRUCT = INITIAL_NONE,
|
||||
CONSTRUCT = INITIAL_CONSTRUCT
|
||||
};
|
||||
|
||||
/*
|
||||
* The js::InvokeArgumentsGuard passed to js_Invoke must come from an
|
||||
* immediately-enclosing successful call to js::StackSpace::pushInvokeArgs,
|
||||
|
@ -159,7 +159,7 @@ InvokeSessionGuard::invoke(JSContext *cx)
|
||||
args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */
|
||||
Probes::enterJSFun(cx, fp->fun(), script_);
|
||||
#ifdef JS_METHODJIT
|
||||
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
|
||||
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_, /* partial = */ false);
|
||||
cx->regs().pc = stop_;
|
||||
#else
|
||||
cx->regs().pc = script_->code;
|
||||
|
@ -5743,7 +5743,7 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee)
|
||||
/* Push a frame for the call. */
|
||||
CallArgs args = CallArgsFromSp(fi.get_argc(), regs.sp);
|
||||
cx->stack.pushInlineFrame(cx, regs, args, *callee, newfun, newscript,
|
||||
MaybeConstructFromBool(fi.is_constructing()),
|
||||
InitialFrameFlagsFromConstructing(fi.is_constructing()),
|
||||
NoCheck());
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -3634,7 +3634,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew, FrameSize
|
||||
|
||||
InlineFrameAssembler inlFrame(masm, callIC, flags);
|
||||
callPatch.hasFastNcode = true;
|
||||
callPatch.fastNcodePatch = inlFrame.assemble(NULL);
|
||||
callPatch.fastNcodePatch = inlFrame.assemble(NULL, PC);
|
||||
|
||||
callIC.hotJump = masm.jump();
|
||||
callIC.joinPoint = callPatch.joinPoint = masm.label();
|
||||
|
@ -102,19 +102,25 @@ class InlineFrameAssembler {
|
||||
tempRegs.takeReg(funObjReg);
|
||||
}
|
||||
|
||||
DataLabelPtr assemble(void *ncode)
|
||||
DataLabelPtr assemble(void *ncode, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT((flags & ~StackFrame::CONSTRUCTING) == 0);
|
||||
|
||||
/* Generate StackFrame::initCallFrameCallerHalf. */
|
||||
|
||||
/* Get the actual flags to write. */
|
||||
JS_ASSERT(!(flags & ~StackFrame::CONSTRUCTING));
|
||||
uint32 flags = this->flags | StackFrame::FUNCTION;
|
||||
if (frameSize.lowered(pc))
|
||||
flags |= StackFrame::LOWERED_CALL_APPLY;
|
||||
|
||||
DataLabelPtr ncodePatch;
|
||||
if (frameSize.isStatic()) {
|
||||
uint32 frameDepth = frameSize.staticLocalSlots();
|
||||
AdjustedFrame newfp(sizeof(StackFrame) + frameDepth * sizeof(Value));
|
||||
|
||||
Address flagsAddr = newfp.addrOf(StackFrame::offsetOfFlags());
|
||||
masm.store32(Imm32(StackFrame::FUNCTION | flags), flagsAddr);
|
||||
masm.store32(Imm32(flags), flagsAddr);
|
||||
Address prevAddr = newfp.addrOf(StackFrame::offsetOfPrev());
|
||||
masm.storePtr(JSFrameReg, prevAddr);
|
||||
Address ncodeAddr = newfp.addrOf(StackFrame::offsetOfNcode());
|
||||
@ -134,7 +140,7 @@ class InlineFrameAssembler {
|
||||
masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.sp)), newfp);
|
||||
|
||||
Address flagsAddr(newfp, StackFrame::offsetOfFlags());
|
||||
masm.store32(Imm32(StackFrame::FUNCTION | flags), flagsAddr);
|
||||
masm.store32(Imm32(flags), flagsAddr);
|
||||
Address prevAddr(newfp, StackFrame::offsetOfPrev());
|
||||
masm.storePtr(JSFrameReg, prevAddr);
|
||||
Address ncodeAddr(newfp, StackFrame::offsetOfNcode());
|
||||
|
@ -250,10 +250,10 @@ stubs::FixupArity(VMFrame &f, uint32 nactual)
|
||||
* members that have been initialized by initJitFrameCallerHalf and the
|
||||
* early prologue.
|
||||
*/
|
||||
MaybeConstruct construct = oldfp->isConstructing();
|
||||
JSFunction *fun = oldfp->fun();
|
||||
JSScript *script = fun->script();
|
||||
void *ncode = oldfp->nativeReturnAddress();
|
||||
InitialFrameFlags initial = oldfp->initialFlags();
|
||||
JSFunction *fun = oldfp->fun();
|
||||
JSScript *script = fun->script();
|
||||
void *ncode = oldfp->nativeReturnAddress();
|
||||
|
||||
/* Pop the inline frame. */
|
||||
f.regs.popPartialFrame((Value *)oldfp);
|
||||
@ -261,7 +261,7 @@ stubs::FixupArity(VMFrame &f, uint32 nactual)
|
||||
/* Reserve enough space for a callee frame. */
|
||||
CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
|
||||
StackFrame *fp = cx->stack.getFixupFrame(cx, f.regs, args, fun, script, ncode,
|
||||
construct, LimitCheck(&f.stackLimit, ncode));
|
||||
initial, LimitCheck(&f.stackLimit, ncode));
|
||||
|
||||
/*
|
||||
* Note: this function is called without f.regs intact, but if the previous
|
||||
@ -293,14 +293,20 @@ stubs::CompileFunction(VMFrame &f, uint32 argc)
|
||||
JS_ASSERT_IF(f.cx->typeInferenceEnabled(), f.stubRejoin);
|
||||
ResetStubRejoin reset(f);
|
||||
|
||||
bool isConstructing = f.fp()->isConstructing();
|
||||
InitialFrameFlags initial = f.fp()->initialFlags();
|
||||
f.regs.popPartialFrame((Value *)f.fp());
|
||||
|
||||
return isConstructing ? UncachedNew(f, argc) : UncachedCall(f, argc);
|
||||
if (InitialFrameFlagsAreConstructing(initial))
|
||||
return UncachedNew(f, argc);
|
||||
else if (InitialFrameFlagsAreLowered(initial))
|
||||
return UncachedLoweredCall(f, argc);
|
||||
else
|
||||
return UncachedCall(f, argc);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
UncachedInlineCall(VMFrame &f, MaybeConstruct construct, void **pret, bool *unjittable, uint32 argc)
|
||||
UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
|
||||
void **pret, bool *unjittable, uint32 argc)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
CallArgs args = CallArgsFromSp(argc, f.regs.sp);
|
||||
@ -308,6 +314,8 @@ UncachedInlineCall(VMFrame &f, MaybeConstruct construct, void **pret, bool *unji
|
||||
JSFunction *newfun = callee.getFunctionPrivate();
|
||||
JSScript *newscript = newfun->script();
|
||||
|
||||
bool construct = InitialFrameFlagsAreConstructing(initial);
|
||||
|
||||
bool newType = construct && cx->typeInferenceEnabled() &&
|
||||
types::UseNewType(cx, f.script(), f.pc());
|
||||
|
||||
@ -324,7 +332,7 @@ UncachedInlineCall(VMFrame &f, MaybeConstruct construct, void **pret, bool *unji
|
||||
|
||||
/* Get pointer to new frame/slots, prepare arguments. */
|
||||
LimitCheck check(&f.stackLimit, NULL);
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, callee, newfun, newscript, construct, check))
|
||||
if (!cx->stack.pushInlineFrame(cx, regs, args, callee, newfun, newscript, initial, check))
|
||||
return false;
|
||||
|
||||
/* Finish the handoff to the new frame regs. */
|
||||
@ -389,7 +397,7 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
||||
/* Try to do a fast inline call before the general Invoke path. */
|
||||
if (IsFunctionObject(args.calleev(), &ucr->fun) && ucr->fun->isInterpretedConstructor()) {
|
||||
ucr->callee = &args.callee();
|
||||
if (!UncachedInlineCall(f, CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
|
||||
if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
|
||||
THROW();
|
||||
} else {
|
||||
if (!InvokeConstructor(cx, args))
|
||||
@ -402,7 +410,15 @@ void * JS_FASTCALL
|
||||
stubs::UncachedCall(VMFrame &f, uint32 argc)
|
||||
{
|
||||
UncachedCallResult ucr;
|
||||
UncachedCallHelper(f, argc, &ucr);
|
||||
UncachedCallHelper(f, argc, false, &ucr);
|
||||
return ucr.codeAddr;
|
||||
}
|
||||
|
||||
void * JS_FASTCALL
|
||||
stubs::UncachedLoweredCall(VMFrame &f, uint32 argc)
|
||||
{
|
||||
UncachedCallResult ucr;
|
||||
UncachedCallHelper(f, argc, true, &ucr);
|
||||
return ucr.codeAddr;
|
||||
}
|
||||
|
||||
@ -427,7 +443,7 @@ stubs::Eval(VMFrame &f, uint32 argc)
|
||||
}
|
||||
|
||||
void
|
||||
stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
||||
stubs::UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallResult *ucr)
|
||||
{
|
||||
ucr->init();
|
||||
|
||||
@ -439,7 +455,8 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
||||
ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
|
||||
|
||||
if (ucr->fun->isInterpreted()) {
|
||||
if (!UncachedInlineCall(f, NO_CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
|
||||
InitialFrameFlags initial = lowered ? INITIAL_LOWERED : INITIAL_NONE;
|
||||
if (!UncachedInlineCall(f, initial, &ucr->codeAddr, &ucr->unjittable, argc))
|
||||
THROW();
|
||||
return;
|
||||
}
|
||||
@ -872,7 +889,7 @@ EvaluateExcessFrame(VMFrame &f, StackFrame *entryFrame)
|
||||
return HandleFinishedFrame(f, entryFrame);
|
||||
|
||||
if (void *ncode = AtSafePoint(cx)) {
|
||||
if (!JaegerShotAtSafePoint(cx, ncode))
|
||||
if (!JaegerShotAtSafePoint(cx, ncode, false))
|
||||
return false;
|
||||
InlineReturn(f);
|
||||
AdvanceReturnPC(cx);
|
||||
@ -1271,8 +1288,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
||||
#endif
|
||||
|
||||
uint32 nextDepth = uint32(-1);
|
||||
|
||||
InterpMode interpMode = JSINTERP_REJOIN;
|
||||
bool skipTrap = false;
|
||||
|
||||
if ((cs->format & (JOF_INC | JOF_DEC)) &&
|
||||
rejoin != REJOIN_FALLTHROUGH &&
|
||||
@ -1359,8 +1375,12 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
||||
break;
|
||||
|
||||
case REJOIN_TRAP:
|
||||
/* Watch out for the case where the TRAP removed itself. */
|
||||
interpMode = untrap.trap ? JSINTERP_SKIP_TRAP : JSINTERP_REJOIN;
|
||||
/*
|
||||
* Make sure when resuming in the interpreter we do not execute the
|
||||
* trap again. Watch out for the case where the TRAP removed itself.
|
||||
*/
|
||||
if (untrap.trap)
|
||||
skipTrap = true;
|
||||
break;
|
||||
|
||||
case REJOIN_FALLTHROUGH:
|
||||
@ -1428,13 +1448,13 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
||||
if (!js::CreateFunCallObject(cx, fp))
|
||||
return js_InternalThrow(f);
|
||||
}
|
||||
|
||||
fp->scopeChain();
|
||||
SetValueRangeToUndefined(fp->slots(), script->nfixed);
|
||||
|
||||
/*
|
||||
* Use the normal interpreter mode, which will construct the 'this'
|
||||
* object if this is a constructor frame.
|
||||
*/
|
||||
interpMode = JSINTERP_NORMAL;
|
||||
/* Construct the 'this' object for the frame if necessary. */
|
||||
if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp)))
|
||||
return js_InternalThrow(f);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1598,26 +1618,10 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
|
||||
nextDepth = analysis->getCode(f.regs.pc).stackDepth;
|
||||
f.regs.sp = fp->base() + nextDepth;
|
||||
|
||||
/* Reinsert any trap before resuming in the interpreter. */
|
||||
untrap.retrap();
|
||||
/* Mark the entry frame as unfinished, and update the regs to resume at. */
|
||||
JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished;
|
||||
cx->compartment->jaegerCompartment()->setLastUnfinished(status);
|
||||
*f.oldregs = f.regs;
|
||||
|
||||
/* Release lock on analysis data before resuming. */
|
||||
enter.leave();
|
||||
|
||||
if (!Interpret(cx, NULL, interpMode))
|
||||
return js_InternalThrow(f);
|
||||
|
||||
/* The interpreter should have finished its entry frame. */
|
||||
JS_ASSERT(f.regs.fp() == fp);
|
||||
|
||||
/* Force construction of the frame's return value, if it was not set. */
|
||||
fp->returnValue();
|
||||
|
||||
/*
|
||||
* The frame is done, but if it finished in the interpreter the call/args
|
||||
* objects need to be detached from the frame.
|
||||
*/
|
||||
fp->putActivationObjects();
|
||||
|
||||
return fp->nativeReturnAddress();
|
||||
return NULL;
|
||||
}
|
||||
|
@ -80,28 +80,6 @@ CanMethodJIT(JSContext *cx, JSScript *script, StackFrame *fp, CompileRequest req
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
RecursiveMethodJIT(JSContext *cx, StackFrame *fp)
|
||||
{
|
||||
if (!cx->compartment->hasJaegerCompartment())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We can recursively enter the method JIT on a single stack frame by
|
||||
* taking back edges, compiling, getting kicked back into the interpreter
|
||||
* and repeating. Watch for this case here, and finish the frame in the
|
||||
* interpreter. :XXX: should be more robust.
|
||||
*/
|
||||
static const unsigned RECURSIVE_METHODJIT_LIMIT = 10;
|
||||
VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
|
||||
for (unsigned i = 0; i < RECURSIVE_METHODJIT_LIMIT; i++) {
|
||||
if (!f || f->entryfp != fp)
|
||||
return false;
|
||||
f = f->previous;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from a backedge in the interpreter to decide if we should transition to the
|
||||
* methodjit. If so, we compile the given function.
|
||||
@ -109,7 +87,7 @@ RecursiveMethodJIT(JSContext *cx, StackFrame *fp)
|
||||
static inline CompileStatus
|
||||
CanMethodJITAtBranch(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecode *pc)
|
||||
{
|
||||
if (!cx->methodJitEnabled || RecursiveMethodJIT(cx, fp))
|
||||
if (!cx->methodJitEnabled)
|
||||
return Compile_Abort;
|
||||
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
|
||||
if (status == JITScript_Invalid)
|
||||
|
@ -132,7 +132,6 @@ extern "C" void JS_FASTCALL
|
||||
PushActiveVMFrame(VMFrame &f)
|
||||
{
|
||||
f.entryfp->script()->compartment->jaegerCompartment()->pushActiveFrame(&f);
|
||||
f.entryncode = f.entryfp->nativeReturnAddress();
|
||||
f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
|
||||
f.regs.clearInlined();
|
||||
}
|
||||
@ -141,12 +140,13 @@ extern "C" void JS_FASTCALL
|
||||
PopActiveVMFrame(VMFrame &f)
|
||||
{
|
||||
f.entryfp->script()->compartment->jaegerCompartment()->popActiveFrame();
|
||||
f.entryfp->setNativeReturnAddress(f.entryncode);
|
||||
}
|
||||
|
||||
extern "C" void JS_FASTCALL
|
||||
SetVMFrameRegs(VMFrame &f)
|
||||
{
|
||||
f.oldregs = &f.cx->stack.regs();
|
||||
|
||||
/* Restored on exit from EnterMethodJIT. */
|
||||
f.cx->stack.repointRegs(&f.regs);
|
||||
}
|
||||
@ -838,6 +838,7 @@ JaegerCompartment::Initialize()
|
||||
#endif
|
||||
|
||||
activeFrame_ = NULL;
|
||||
lastUnfinished_ = (JaegerStatus) 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -860,8 +861,8 @@ JaegerCompartment::Finish()
|
||||
extern "C" JSBool
|
||||
JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit);
|
||||
|
||||
JSBool
|
||||
mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit)
|
||||
JaegerStatus
|
||||
mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit, bool partial)
|
||||
{
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
Profiler prof;
|
||||
@ -875,10 +876,6 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi
|
||||
JS_ASSERT(cx->fp() == fp);
|
||||
FrameRegs &oldRegs = cx->regs();
|
||||
|
||||
fp->scopeChain();
|
||||
if (fp->isFunctionFrame() && fp->script()->usesArguments)
|
||||
fp->ensureCoherentArgCount();
|
||||
|
||||
JSBool ok;
|
||||
{
|
||||
AssertCompartmentUnchanged pcc(cx);
|
||||
@ -886,8 +883,39 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi
|
||||
ok = JaegerTrampoline(cx, fp, code, stackLimit);
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
prof.stop();
|
||||
JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
|
||||
#endif
|
||||
|
||||
/* Undo repointRegs in SetVMFrameRegs. */
|
||||
cx->stack.repointRegs(&oldRegs);
|
||||
|
||||
JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished();
|
||||
if (status) {
|
||||
if (partial) {
|
||||
/*
|
||||
* Being called from the interpreter, which will resume execution
|
||||
* where the JIT left off.
|
||||
*/
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call back into the interpreter to finish the initial frame. This may
|
||||
* invoke EnterMethodJIT again, but will allow partial execution for
|
||||
* that recursive invocation, so we can have at most two VM frames for
|
||||
* a range of inline frames.
|
||||
*/
|
||||
InterpMode mode = (status == Jaeger_UnfinishedAtTrap)
|
||||
? JSINTERP_SKIP_TRAP
|
||||
: JSINTERP_REJOIN;
|
||||
ok = Interpret(cx, fp, mode);
|
||||
|
||||
return ok ? Jaeger_Returned : Jaeger_Throwing;
|
||||
}
|
||||
|
||||
/* The entry frame should have finished. */
|
||||
JS_ASSERT(fp == cx->fp());
|
||||
|
||||
if (ok) {
|
||||
@ -898,30 +926,25 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi
|
||||
/* See comment in mjit::Compiler::emitReturn. */
|
||||
fp->markActivationObjectsAsPut();
|
||||
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
prof.stop();
|
||||
JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
|
||||
#endif
|
||||
|
||||
return ok;
|
||||
return ok ? Jaeger_Returned : Jaeger_Throwing;
|
||||
}
|
||||
|
||||
static inline JSBool
|
||||
CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code)
|
||||
static inline JaegerStatus
|
||||
CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, bool partial)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
JS_CHECK_RECURSION(cx, return Jaeger_Throwing);
|
||||
|
||||
JS_ASSERT(!cx->compartment->activeAnalysis);
|
||||
|
||||
Value *stackLimit = cx->stack.space().getStackLimit(cx);
|
||||
if (!stackLimit)
|
||||
return false;
|
||||
return Jaeger_Throwing;
|
||||
|
||||
return EnterMethodJIT(cx, fp, code, stackLimit);
|
||||
return EnterMethodJIT(cx, fp, code, stackLimit, partial);
|
||||
}
|
||||
|
||||
JSBool
|
||||
mjit::JaegerShot(JSContext *cx)
|
||||
JaegerStatus
|
||||
mjit::JaegerShot(JSContext *cx, bool partial)
|
||||
{
|
||||
StackFrame *fp = cx->fp();
|
||||
JSScript *script = fp->script();
|
||||
@ -934,17 +957,17 @@ mjit::JaegerShot(JSContext *cx)
|
||||
|
||||
JS_ASSERT(cx->regs().pc == script->code);
|
||||
|
||||
return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
|
||||
return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry, partial);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
|
||||
JaegerStatus
|
||||
js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial)
|
||||
{
|
||||
#ifdef JS_TRACER
|
||||
JS_ASSERT(!TRACE_RECORDER(cx));
|
||||
#endif
|
||||
|
||||
return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
|
||||
return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint, partial);
|
||||
}
|
||||
|
||||
NativeMapEntry *
|
||||
|
@ -112,7 +112,7 @@ struct VMFrame
|
||||
JSContext *cx;
|
||||
Value *stackLimit;
|
||||
StackFrame *entryfp;
|
||||
void *entryncode;
|
||||
FrameRegs *oldregs;
|
||||
JSRejoinState stubRejoin; /* How to rejoin if inside a call from an IC stub. */
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
@ -354,6 +354,29 @@ struct Trampolines {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Result status of executing mjit code on a frame. */
|
||||
enum JaegerStatus
|
||||
{
|
||||
/* Entry frame finished, and is throwing an exception. */
|
||||
Jaeger_Throwing = 0,
|
||||
|
||||
/* Entry frame finished, and is returning. */
|
||||
Jaeger_Returned = 1,
|
||||
|
||||
/*
|
||||
* Entry frame did not finish. cx->regs reflects where to resume execution.
|
||||
* This result is only possible if 'partial' is passed as true below.
|
||||
*/
|
||||
Jaeger_Unfinished = 2,
|
||||
|
||||
/*
|
||||
* As for Unfinished, but stopped after a TRAP triggered recompilation.
|
||||
* The trap has been reinstalled, but should not execute again when
|
||||
* resuming execution.
|
||||
*/
|
||||
Jaeger_UnfinishedAtTrap = 3
|
||||
};
|
||||
|
||||
/*
|
||||
* Method JIT compartment data. Currently, there is exactly one per
|
||||
* JS compartment. It would be safe for multiple JS compartments to
|
||||
@ -364,6 +387,8 @@ class JaegerCompartment {
|
||||
JSC::ExecutableAllocator *execAlloc_; // allocator for jit code
|
||||
Trampolines trampolines; // force-return trampolines
|
||||
VMFrame *activeFrame_; // current active VMFrame
|
||||
JaegerStatus lastUnfinished_;// result status of last VM frame,
|
||||
// if unfinished
|
||||
|
||||
void Finish();
|
||||
|
||||
@ -382,6 +407,7 @@ class JaegerCompartment {
|
||||
}
|
||||
|
||||
void pushActiveFrame(VMFrame *f) {
|
||||
JS_ASSERT(!lastUnfinished_);
|
||||
f->previous = activeFrame_;
|
||||
f->scratch = NULL;
|
||||
activeFrame_ = f;
|
||||
@ -392,6 +418,17 @@ class JaegerCompartment {
|
||||
activeFrame_ = activeFrame_->previous;
|
||||
}
|
||||
|
||||
void setLastUnfinished(JaegerStatus status) {
|
||||
JS_ASSERT(!lastUnfinished_);
|
||||
lastUnfinished_ = status;
|
||||
}
|
||||
|
||||
JaegerStatus lastUnfinished() {
|
||||
JaegerStatus result = lastUnfinished_;
|
||||
lastUnfinished_ = (JaegerStatus) 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *forceReturnFromExternC() const {
|
||||
return JS_FUNC_TO_DATA_PTR(void *, trampolines.forceReturn);
|
||||
}
|
||||
@ -601,13 +638,14 @@ struct JITScript {
|
||||
* Execute the given mjit code. This is a low-level call and callers must
|
||||
* provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
|
||||
*/
|
||||
JSBool EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit);
|
||||
JaegerStatus EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit,
|
||||
bool partial);
|
||||
|
||||
/* Execute a method that has been JIT compiled. */
|
||||
JSBool JaegerShot(JSContext *cx);
|
||||
JaegerStatus JaegerShot(JSContext *cx, bool partial);
|
||||
|
||||
/* Drop into the middle of a method at an arbitrary point, and execute. */
|
||||
JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint);
|
||||
JaegerStatus JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial);
|
||||
|
||||
enum CompileStatus
|
||||
{
|
||||
|
@ -637,7 +637,8 @@ class CallCompiler : public BaseCompiler
|
||||
RegisterID t0 = inlFrame.tempRegs.takeAnyReg().reg();
|
||||
|
||||
/* Generate the inline frame creation. */
|
||||
inlFrame.assemble(ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress());
|
||||
void *ncode = ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress();
|
||||
inlFrame.assemble(ncode, f.pc());
|
||||
|
||||
/* funPtrReg is still valid. Check if a compilation is needed. */
|
||||
Address scriptAddr(ic.funPtrReg, offsetof(JSFunction, u) +
|
||||
@ -683,7 +684,7 @@ class CallCompiler : public BaseCompiler
|
||||
masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.sp)), JSFrameReg);
|
||||
|
||||
/* Compute the value of ncode to use at this call site. */
|
||||
uint8 *ncode = (uint8 *) f.jit()->code.m_code.executableAddress() + ic.call->codeOffset;
|
||||
ncode = (uint8 *) f.jit()->code.m_code.executableAddress() + ic.call->codeOffset;
|
||||
masm.storePtr(ImmPtr(ncode), Address(JSFrameReg, StackFrame::offsetOfNcode()));
|
||||
|
||||
masm.jump(Registers::ReturnReg);
|
||||
@ -1064,11 +1065,14 @@ class CallCompiler : public BaseCompiler
|
||||
JITScript *jit = fp->jit();
|
||||
RecompilationMonitor monitor(cx);
|
||||
|
||||
bool lowered = ic.frameSize.lowered(f.pc());
|
||||
JS_ASSERT_IF(lowered, !callingNew);
|
||||
|
||||
stubs::UncachedCallResult ucr;
|
||||
if (callingNew)
|
||||
stubs::UncachedNewHelper(f, ic.frameSize.staticArgc(), &ucr);
|
||||
else
|
||||
stubs::UncachedCallHelper(f, ic.frameSize.getArgc(f), &ucr);
|
||||
stubs::UncachedCallHelper(f, ic.frameSize.getArgc(f), lowered, &ucr);
|
||||
|
||||
// Watch out in case the IC was invalidated by a recompilation on the calling
|
||||
// script. This can happen either if the callee is executed or if it compiles
|
||||
|
@ -102,6 +102,10 @@ class FrameSize
|
||||
}
|
||||
return native ? REJOIN_NATIVE_LOWERED : REJOIN_CALL_PROLOGUE_LOWERED_APPLY;
|
||||
}
|
||||
|
||||
bool lowered(jsbytecode *pc) {
|
||||
return !isStatic() || staticArgc() != GET_ARGC(pc);
|
||||
}
|
||||
};
|
||||
|
||||
namespace ic {
|
||||
|
@ -408,10 +408,6 @@ Recompiler::recompile(bool resetUses)
|
||||
if (JITCodeReturnAddress(*addr)) {
|
||||
JS_ASSERT(fp->jit()->isValidCode(*addr));
|
||||
patchCall(fp->jit(), fp, addr);
|
||||
} else if (nextf && nextf->entryfp == next &&
|
||||
JITCodeReturnAddress(nextf->entryncode)) {
|
||||
JS_ASSERT(fp->jit()->isValidCode(nextf->entryncode));
|
||||
patchCall(fp->jit(), fp, &nextf->entryncode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,7 @@ void JS_FASTCALL SlowNew(VMFrame &f, uint32 argc);
|
||||
void JS_FASTCALL SlowCall(VMFrame &f, uint32 argc);
|
||||
void * JS_FASTCALL UncachedNew(VMFrame &f, uint32 argc);
|
||||
void * JS_FASTCALL UncachedCall(VMFrame &f, uint32 argc);
|
||||
void * JS_FASTCALL UncachedLoweredCall(VMFrame &f, uint32 argc);
|
||||
void JS_FASTCALL Eval(VMFrame &f, uint32 argc);
|
||||
void JS_FASTCALL ScriptDebugPrologue(VMFrame &f);
|
||||
void JS_FASTCALL ScriptDebugEpilogue(VMFrame &f);
|
||||
@ -107,7 +108,7 @@ struct UncachedCallResult {
|
||||
* These functions either execute the function, return a native code
|
||||
* pointer that can be used to call the function, or throw.
|
||||
*/
|
||||
void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
|
||||
void UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallResult *ucr);
|
||||
void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
|
||||
|
||||
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
|
||||
|
@ -80,6 +80,10 @@ StackFrame::resetGeneratorPrev(JSContext *cx)
|
||||
inline void
|
||||
StackFrame::initInlineFrame(JSFunction *fun, StackFrame *prevfp, jsbytecode *prevpc)
|
||||
{
|
||||
/*
|
||||
* Note: no need to ensure the scopeChain is instantiated for inline
|
||||
* frames. Functions which use the scope chain are never inlined.
|
||||
*/
|
||||
flags_ = StackFrame::FUNCTION;
|
||||
exec.fun = fun;
|
||||
resetInlinePrev(prevfp, prevpc);
|
||||
@ -100,6 +104,7 @@ StackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
||||
JSScript *script, uint32 nactual, StackFrame::Flags flagsArg)
|
||||
{
|
||||
JS_ASSERT((flagsArg & ~(CONSTRUCTING |
|
||||
LOWERED_CALL_APPLY |
|
||||
OVERFLOW_ARGS |
|
||||
UNDERFLOW_ARGS)) == 0);
|
||||
JS_ASSERT(fun == callee.getFunctionPrivate());
|
||||
@ -108,7 +113,7 @@ StackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
||||
/* Initialize stack frame members. */
|
||||
flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | flagsArg;
|
||||
exec.fun = fun;
|
||||
args.nactual = nactual; /* only need to write if over/under-flow */
|
||||
args.nactual = nactual;
|
||||
scopeChain_ = callee.getParent();
|
||||
ncode_ = NULL;
|
||||
initPrev(cx);
|
||||
@ -142,19 +147,9 @@ StackFrame::resetCallFrame(JSScript *script)
|
||||
HAS_SCOPECHAIN |
|
||||
HAS_ANNOTATION |
|
||||
HAS_HOOK_DATA |
|
||||
HAS_CALL_OBJ |
|
||||
HAS_ARGS_OBJ |
|
||||
FINISHED_IN_INTERP |
|
||||
DOWN_FRAMES_EXPANDED)));
|
||||
|
||||
/*
|
||||
* Since the stack frame is usually popped after PutActivationObjects,
|
||||
* these bits aren't cleared. The activation objects must have actually
|
||||
* been put, though.
|
||||
*/
|
||||
JS_ASSERT_IF(flags_ & HAS_CALL_OBJ, callObj().getPrivate() == NULL);
|
||||
JS_ASSERT_IF(flags_ & HAS_ARGS_OBJ, argsObj().getPrivate() == NULL);
|
||||
|
||||
flags_ &= FUNCTION |
|
||||
OVERFLOW_ARGS |
|
||||
HAS_PREVPC |
|
||||
@ -171,6 +166,7 @@ StackFrame::initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags,
|
||||
void *ncode)
|
||||
{
|
||||
JS_ASSERT((flags & ~(CONSTRUCTING |
|
||||
LOWERED_CALL_APPLY |
|
||||
FUNCTION |
|
||||
OVERFLOW_ARGS |
|
||||
UNDERFLOW_ARGS)) == 0);
|
||||
@ -188,8 +184,7 @@ inline void
|
||||
StackFrame::initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual)
|
||||
{
|
||||
exec.fun = fun;
|
||||
if (flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS))
|
||||
args.nactual = nactual;
|
||||
args.nactual = nactual;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -210,6 +205,7 @@ StackFrame::initJitFrameLatePrologue(JSContext *cx, Value **limit)
|
||||
}
|
||||
}
|
||||
|
||||
scopeChain();
|
||||
SetValueRangeToUndefined(slots(), script()->nfixed);
|
||||
return true;
|
||||
}
|
||||
@ -287,19 +283,20 @@ struct CopyTo
|
||||
inline uintN
|
||||
StackFrame::numActualArgs() const
|
||||
{
|
||||
/*
|
||||
* args.nactual is always coherent, except for method JIT frames where the
|
||||
* callee does not access its arguments and the number of actual arguments
|
||||
* matches the number of formal arguments. The JIT requires that all frames
|
||||
* which do not have an arguments object and use their arguments have a
|
||||
* coherent args.nactual (even though the below code may not use it), as
|
||||
* JIT code may access the field directly.
|
||||
*/
|
||||
JS_ASSERT(hasArgs());
|
||||
if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
|
||||
return hasArgsObj() ? argsObj().initialLength() : args.nactual;
|
||||
return numFormalArgs();
|
||||
}
|
||||
|
||||
inline void
|
||||
StackFrame::ensureCoherentArgCount()
|
||||
{
|
||||
if (!hasArgsObj())
|
||||
args.nactual = numActualArgs();
|
||||
}
|
||||
|
||||
inline Value *
|
||||
StackFrame::actualArgs() const
|
||||
{
|
||||
@ -545,7 +542,7 @@ template <class Check>
|
||||
JS_ALWAYS_INLINE bool
|
||||
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check)
|
||||
InitialFrameFlags initial, Check check)
|
||||
{
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(regs.sp == args.end());
|
||||
@ -553,7 +550,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
||||
JS_ASSERT(callee.getFunctionPrivate() == fun);
|
||||
JS_ASSERT(fun->script() == script);
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame::Flags flags = ToFrameFlags(initial);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
|
||||
if (!fp)
|
||||
return false;
|
||||
@ -572,7 +569,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &ar
|
||||
JS_ALWAYS_INLINE StackFrame *
|
||||
ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check)
|
||||
InitialFrameFlags initial, LimitCheck check)
|
||||
{
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(®s == &cx->regs());
|
||||
@ -580,7 +577,7 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args
|
||||
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
|
||||
JS_ASSERT(fun->script() == script);
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame::Flags flags = ToFrameFlags(initial);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
@ -605,7 +605,7 @@ ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
|
||||
|
||||
bool
|
||||
ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
MaybeConstruct construct, InvokeFrameGuard *ifg)
|
||||
InitialFrameFlags initial, InvokeFrameGuard *ifg)
|
||||
{
|
||||
JS_ASSERT(onTop());
|
||||
JS_ASSERT(space().firstUnused() == args.end());
|
||||
@ -614,7 +614,7 @@ ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
JSFunction *fun = callee.getFunctionPrivate();
|
||||
JSScript *script = fun->script();
|
||||
|
||||
StackFrame::Flags flags = ToFrameFlags(construct);
|
||||
StackFrame::Flags flags = ToFrameFlags(initial);
|
||||
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
|
||||
if (!fp)
|
||||
return false;
|
||||
@ -960,13 +960,11 @@ StackIter::settleOnNewState()
|
||||
if (op == JSOP_CALL || op == JSOP_FUNCALL) {
|
||||
uintN argc = GET_ARGC(pc_);
|
||||
DebugOnly<uintN> spoff = sp_ - fp_->base();
|
||||
#if 0
|
||||
#ifdef DEBUG
|
||||
if (cx_->stackIterAssertionEnabled) {
|
||||
JS_ASSERT_IF(!fp_->hasImacropc(),
|
||||
spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Value *vp = sp_ - (2 + argc);
|
||||
|
||||
|
@ -303,9 +303,11 @@ CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
enum MaybeConstruct {
|
||||
NO_CONSTRUCT = 0, /* == false */
|
||||
CONSTRUCT = 0x80 /* == StackFrame::CONSTRUCTING, asserted below */
|
||||
/* Flags specified for a frame as it is constructed. */
|
||||
enum InitialFrameFlags {
|
||||
INITIAL_NONE = 0,
|
||||
INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted below */
|
||||
INITIAL_LOWERED = 0x400000 /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
|
||||
};
|
||||
|
||||
enum ExecuteType {
|
||||
@ -352,7 +354,9 @@ class StackFrame
|
||||
HAS_SCOPECHAIN = 0x80000, /* frame has scopeChain_ set */
|
||||
HAS_PREVPC = 0x100000, /* frame has prevpc_ and prevInline_ set */
|
||||
|
||||
DOWN_FRAMES_EXPANDED = 0x200000 /* inlining in down frames has been expanded */
|
||||
/* Method JIT state */
|
||||
DOWN_FRAMES_EXPANDED = 0x200000, /* inlining in down frames has been expanded */
|
||||
LOWERED_CALL_APPLY = 0x400000 /* Pushed by a lowered call/apply */
|
||||
};
|
||||
|
||||
private:
|
||||
@ -677,7 +681,6 @@ class StackFrame
|
||||
inline uintN numActualArgs() const;
|
||||
inline Value *actualArgs() const;
|
||||
inline Value *actualArgsEnd() const;
|
||||
inline void ensureCoherentArgCount();
|
||||
|
||||
inline Value &canonicalActualArg(uintN i) const;
|
||||
template <class Op>
|
||||
@ -810,6 +813,12 @@ class StackFrame
|
||||
* when setting the scope chain, to indicate whether the new scope chain
|
||||
* contains a new call object and thus changes the 'hasCallObj' state.
|
||||
*
|
||||
* The method JIT requires that HAS_SCOPECHAIN be set for all frames which
|
||||
* use NAME or related opcodes that can access the scope chain (so it does
|
||||
* not have to test the bit). To ensure this, we always initialize the
|
||||
* scope chain when pushing frames in the VM, and only initialize it when
|
||||
* pushing frames in JIT code when the above situation applies.
|
||||
*
|
||||
* NB: 'fp->hasCallObj()' implies that fp->callObj() needs to be 'put' when
|
||||
* the frame is popped. Since the scope chain of a non-strict eval frame
|
||||
* contains the call object of the parent (function) frame, it is possible
|
||||
@ -937,7 +946,7 @@ class StackFrame
|
||||
}
|
||||
|
||||
bool downFramesExpanded() {
|
||||
return flags_ & DOWN_FRAMES_EXPANDED;
|
||||
return !!(flags_ & DOWN_FRAMES_EXPANDED);
|
||||
}
|
||||
|
||||
/* Debugger hook data */
|
||||
@ -1041,10 +1050,27 @@ class StackFrame
|
||||
* Other flags
|
||||
*/
|
||||
|
||||
MaybeConstruct isConstructing() const {
|
||||
JS_STATIC_ASSERT((int)CONSTRUCT == (int)CONSTRUCTING);
|
||||
JS_STATIC_ASSERT((int)NO_CONSTRUCT == 0);
|
||||
return MaybeConstruct(flags_ & CONSTRUCTING);
|
||||
InitialFrameFlags initialFlags() const {
|
||||
JS_STATIC_ASSERT((int)INITIAL_NONE == 0);
|
||||
JS_STATIC_ASSERT((int)INITIAL_CONSTRUCT == (int)CONSTRUCTING);
|
||||
JS_STATIC_ASSERT((int)INITIAL_LOWERED == (int)LOWERED_CALL_APPLY);
|
||||
uint32 mask = CONSTRUCTING | LOWERED_CALL_APPLY;
|
||||
JS_ASSERT((flags_ & mask) != mask);
|
||||
return InitialFrameFlags(flags_ & mask);
|
||||
}
|
||||
|
||||
bool isConstructing() const {
|
||||
return !!(flags_ & CONSTRUCTING);
|
||||
}
|
||||
|
||||
/*
|
||||
* The method JIT call/apply optimization can erase Function.{call,apply}
|
||||
* invocations from the stack and push the callee frame directly. The base
|
||||
* of these frames will be offset by one value, however, which the
|
||||
* interpreter needs to account for if it ends up popping the frame.
|
||||
*/
|
||||
bool loweredCallOrApply() const {
|
||||
return !!(flags_ & LOWERED_CALL_APPLY);
|
||||
}
|
||||
|
||||
bool isDebuggerFrame() const {
|
||||
@ -1160,23 +1186,33 @@ class StackFrame
|
||||
static const size_t VALUES_PER_STACK_FRAME = sizeof(StackFrame) / sizeof(Value);
|
||||
|
||||
static inline uintN
|
||||
ToReportFlags(MaybeConstruct construct)
|
||||
ToReportFlags(InitialFrameFlags initial)
|
||||
{
|
||||
return uintN(construct);
|
||||
return uintN(initial & StackFrame::CONSTRUCTING);
|
||||
}
|
||||
|
||||
static inline StackFrame::Flags
|
||||
ToFrameFlags(MaybeConstruct construct)
|
||||
ToFrameFlags(InitialFrameFlags initial)
|
||||
{
|
||||
JS_STATIC_ASSERT((int)CONSTRUCT == (int)StackFrame::CONSTRUCTING);
|
||||
JS_STATIC_ASSERT((int)NO_CONSTRUCT == 0);
|
||||
return StackFrame::Flags(construct);
|
||||
return StackFrame::Flags(initial);
|
||||
}
|
||||
|
||||
static inline MaybeConstruct
|
||||
MaybeConstructFromBool(bool b)
|
||||
static inline InitialFrameFlags
|
||||
InitialFrameFlagsFromConstructing(bool b)
|
||||
{
|
||||
return b ? CONSTRUCT : NO_CONSTRUCT;
|
||||
return b ? INITIAL_CONSTRUCT : INITIAL_NONE;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
InitialFrameFlagsAreConstructing(InitialFrameFlags initial)
|
||||
{
|
||||
return !!(initial & INITIAL_CONSTRUCT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
InitialFrameFlagsAreLowered(InitialFrameFlags initial)
|
||||
{
|
||||
return !!(initial & INITIAL_LOWERED);
|
||||
}
|
||||
|
||||
inline StackFrame * Valueify(JSStackFrame *fp) { return (StackFrame *)fp; }
|
||||
@ -1582,7 +1618,7 @@ class ContextStack
|
||||
|
||||
/* Called by Invoke for a scripted function call. */
|
||||
bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
|
||||
MaybeConstruct construct, InvokeFrameGuard *ifg);
|
||||
InitialFrameFlags initial, InvokeFrameGuard *ifg);
|
||||
|
||||
/* Called by Execute for execution of eval or global code. */
|
||||
bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
|
||||
@ -1608,7 +1644,7 @@ class ContextStack
|
||||
template <class Check>
|
||||
bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSObject &callee, JSFunction *fun, JSScript *script,
|
||||
MaybeConstruct construct, Check check);
|
||||
InitialFrameFlags initial, Check check);
|
||||
void popInlineFrame(FrameRegs ®s);
|
||||
|
||||
/* Pop a partially-pushed frame after hitting the limit before throwing. */
|
||||
@ -1630,7 +1666,7 @@ class ContextStack
|
||||
*/
|
||||
StackFrame *getFixupFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args,
|
||||
JSFunction *fun, JSScript *script, void *ncode,
|
||||
MaybeConstruct construct, LimitCheck check);
|
||||
InitialFrameFlags flags, LimitCheck check);
|
||||
|
||||
bool saveFrameChain();
|
||||
void restoreFrameChain();
|
||||
|
Loading…
Reference in New Issue
Block a user