Allow JaegerShot to only partially execute frames, bug 665815. r=luke

This commit is contained in:
Brian Hackett 2011-07-07 21:02:57 -07:00
parent 8453d326d9
commit eb4a0fc5ea
21 changed files with 336 additions and 184 deletions

View 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();

View 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();

View File

@ -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 *

View File

@ -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
/////////////////////////////////////////////////////////////////////

View File

@ -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() == &regs); \
@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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());

View File

@ -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;
}

View File

@ -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)

View File

@ -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 *

View File

@ -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
{

View File

@ -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

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 &regs, 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 &regs, 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 &regs, const CallArgs &ar
JS_ALWAYS_INLINE StackFrame *
ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check)
InitialFrameFlags initial, LimitCheck check)
{
JS_ASSERT(onTop());
JS_ASSERT(&regs == &cx->regs());
@ -580,7 +577,7 @@ ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, 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;

View File

@ -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);

View File

@ -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 &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Check check);
InitialFrameFlags initial, Check check);
void popInlineFrame(FrameRegs &regs);
/* Pop a partially-pushed frame after hitting the limit before throwing. */
@ -1630,7 +1666,7 @@ class ContextStack
*/
StackFrame *getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check);
InitialFrameFlags flags, LimitCheck check);
bool saveFrameChain();
void restoreFrameChain();