Remove JSOP_BEGIN and fix tracer integration issues (bug 603044, r=luke+dmandelin).

This commit is contained in:
David Anderson 2010-10-15 11:36:56 -07:00
parent f0142c8cfd
commit 4fb8c385ad
23 changed files with 539 additions and 380 deletions

View File

@ -2008,7 +2008,7 @@ struct JSContext
public:
friend class js::StackSpace;
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, uintN);
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
void resetCompartment();

View File

@ -235,12 +235,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
return JS_FALSE;
}
if (JSOp(*pc) == JSOP_BEGIN) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
NULL, JSMSG_READ_ONLY, "trap invalid on BEGIN opcode");
return JS_FALSE;
}
JS_ASSERT((JSOp) *pc != JSOP_TRAP);
junk = NULL;
rt = cx->runtime;
@ -1021,13 +1015,6 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
return js_LineNumberToPC(script, lineno);
}
JS_PUBLIC_API(jsbytecode *)
JS_FirstValidPC(JSContext *cx, JSScript *script)
{
jsbytecode *pc = script->code;
return *pc == JSOP_BEGIN ? pc + JSOP_BEGIN_LENGTH : pc;
}
JS_PUBLIC_API(jsbytecode *)
JS_EndPC(JSContext *cx, JSScript *script)
{

View File

@ -159,9 +159,6 @@ JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
extern JS_PUBLIC_API(jsbytecode *)
JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno);
extern JS_PUBLIC_API(jsbytecode *)
JS_FirstValidPC(JSContext *cx, JSScript *script);
extern JS_PUBLIC_API(jsbytecode *)
JS_EndPC(JSContext *cx, JSScript *script);

View File

@ -3716,15 +3716,10 @@ bad:
JSBool
js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
{
CG_SWITCH_TO_PROLOG(cg);
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
if (js_Emit1(cx, cg, JSOP_BEGIN) < 0)
return false;
CG_SWITCH_TO_MAIN(cg);
if (cg->flags & TCF_FUN_IS_GENERATOR) {
/* JSOP_GENERATOR must be the first real instruction. */
/* JSOP_GENERATOR must be the first instruction. */
CG_SWITCH_TO_PROLOG(cg);
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
return false;
CG_SWITCH_TO_MAIN(cg);

View File

@ -82,7 +82,6 @@
#include "jscntxtinlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsprobes.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
@ -734,28 +733,13 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
}
}
JSInterpreterHook hook = cx->debugHooks->callHook;
void *hookData = NULL;
if (JS_UNLIKELY(hook != NULL))
hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData);
/* Run function until JSOP_STOP, JSOP_RETURN or error. */
JSBool ok;
{
AutoPreserveEnumerators preserve(cx);
Probes::enterJSFun(cx, fun);
ok = RunScript(cx, script, fp);
Probes::exitJSFun(cx, fun);
}
if (JS_UNLIKELY(hookData != NULL)) {
hook = cx->debugHooks->callHook;
if (hook)
hook(cx, fp, JS_FALSE, &ok, hookData);
}
PutActivationObjects(cx, fp);
args.rval() = fp->returnValue();
JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive());
@ -2161,11 +2145,28 @@ IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
return js_IteratorNext(cx, iterobj, rval);
}
static inline bool
ScriptPrologue(JSContext *cx, JSStackFrame *fp)
{
if (fp->isConstructing()) {
JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
if (!obj)
return false;
fp->functionThis().setObject(*obj);
}
JSInterpreterHook hook = cx->debugHooks->callHook;
if (JS_UNLIKELY(hook != NULL))
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
Probes::enterJSFun(cx, fp->maybeFun());
return true;
}
namespace js {
JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN interpFlags)
Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
{
#ifdef MOZ_TRACEVIS
TraceVisStateObj tvso(cx, S_INTERP);
@ -2396,7 +2397,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
JS_ASSERT(!TRACE_RECORDER(cx)); \
interpReturnOK = true; \
goto stop_recording; \
goto leave_on_safe_point; \
} \
} while (0)
#else
@ -2432,13 +2433,25 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
/* Check for too deep of a native thread stack. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
MUST_FLOW_THROUGH("exit");
++cx->interpLevel;
JSFrameRegs regs = *cx->regs;
/* Repoint cx->regs to a local variable for faster access. */
JSFrameRegs *const prevContextRegs = cx->regs;
JSFrameRegs regs = *cx->regs;
cx->setCurrentRegs(&regs);
struct InterpExitGuard {
JSContext *cx;
const JSFrameRegs &regs;
JSFrameRegs *prevContextRegs;
InterpExitGuard(JSContext *cx, JSFrameRegs &regs)
: cx(cx), regs(regs), prevContextRegs(cx->regs) {
cx->setCurrentRegs(&regs);
++cx->interpLevel;
}
~InterpExitGuard() {
--cx->interpLevel;
JS_ASSERT(cx->regs == &regs);
*prevContextRegs = regs;
cx->setCurrentRegs(prevContextRegs);
}
} interpGuard(cx, regs);
/* Copy in hot values that change infrequently. */
JSRuntime *const rt = cx->runtime;
@ -2450,7 +2463,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
JS_ASSERT(script->length > 1);
#if defined(JS_TRACER) && defined(JS_METHODJIT)
bool leaveOnSafePoint = !!(interpFlags & JSINTERP_SAFEPOINT);
bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
#else
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
@ -2470,7 +2483,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
#if JS_HAS_GENERATORS
if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) {
JS_ASSERT(prevContextRegs == &cx->generatorFor(regs.fp)->regs);
JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs);
JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script));
@ -2489,7 +2502,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
* there should already be a valid recorder. Otherwise...
* we cannot reenter the interpreter while recording.
*/
if (interpFlags & JSINTERP_RECORD) {
if (interpMode == JSINTERP_RECORD) {
JS_ASSERT(TRACE_RECORDER(cx));
ENABLE_INTERRUPTS();
} else if (TRACE_RECORDER(cx)) {
@ -2500,6 +2513,15 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
atoms = COMMON_ATOMS_START(&rt->atomState);
#endif
/* Don't call the script prologue if executing between Method and Trace JIT. */
if (interpMode == JSINTERP_NORMAL) {
JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), regs.pc == script->code);
if (!ScriptPrologue(cx, regs.fp))
goto error;
}
CHECK_INTERRUPT_HANDLER();
/* State communicated between non-local jumps: */
JSBool interpReturnOK;
JSAtom *atomNotDefined;
@ -2585,7 +2607,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
AbortableRecordingStatus status = tr->monitorRecording(op);
JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
if (interpFlags & (JSINTERP_RECORD | JSINTERP_SAFEPOINT)) {
if (interpMode != JSINTERP_NORMAL) {
JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
switch (status) {
case ARECORD_IMACRO_ABORTED:
case ARECORD_ABORTED:
@ -2760,27 +2783,11 @@ BEGIN_CASE(JSOP_STOP)
inline_return:
{
JS_ASSERT(!js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
if (JS_UNLIKELY(regs.fp->hasHookData())) {
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
hook(cx, regs.fp, JS_FALSE, &interpReturnOK, regs.fp->hookData());
CHECK_INTERRUPT_HANDLER();
}
}
PutActivationObjects(cx, regs.fp);
Probes::exitJSFun(cx, regs.fp->maybeFun());
/*
* If inline-constructing, replace primitive rval with the new object
* passed in via |this|, and instrument this constructor invocation.
*/
if (regs.fp->isConstructing()) {
if (regs.fp->returnValue().isPrimitive())
regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis()));
JS_RUNTIME_METER(cx->runtime, constructs);
}
interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
CHECK_INTERRUPT_HANDLER();
/* The JIT inlines ScriptEpilogue. */
jit_return:
Value *newsp = regs.fp->actualArgs() - 1;
newsp[-1] = regs.fp->returnValue();
cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp);
@ -2803,18 +2810,6 @@ BEGIN_CASE(JSOP_STOP)
goto error;
} else {
JS_ASSERT(regs.sp == regs.fp->base());
if (regs.fp->isConstructing() && regs.fp->returnValue().isPrimitive())
regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis()));
#if defined(JS_TRACER) && defined(JS_METHODJIT)
/* Hack: re-push rval so either JIT will read it properly. */
regs.fp->setBailedAtReturn();
if (TRACE_RECORDER(cx)) {
AbortRecording(cx, "recording out of Interpret");
interpReturnOK = true;
goto stop_recording;
}
#endif
}
interpReturnOK = true;
goto exit;
@ -4569,41 +4564,6 @@ BEGIN_CASE(JSOP_ENUMELEM)
}
END_CASE(JSOP_ENUMELEM)
BEGIN_CASE(JSOP_BEGIN)
{
if (regs.fp->isConstructing()) {
JSObject *obj2 = js_CreateThisForFunction(cx, &regs.fp->callee());
if (!obj2)
goto error;
regs.fp->functionThis().setObject(*obj2);
}
/* Call the debugger hook if present. */
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0,
cx->debugHooks->callHookData));
CHECK_INTERRUPT_HANDLER();
}
JS_RUNTIME_METER(rt, inlineCalls);
Probes::enterJSFun(cx, regs.fp->fun());
#ifdef JS_METHODJIT
/* Try to ensure methods are method JIT'd. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp);
if (status == mjit::Compile_Error)
goto error;
if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) {
if (!mjit::JaegerShot(cx))
goto error;
interpReturnOK = true;
goto inline_return;
}
#endif
}
END_CASE(JSOP_BEGIN)
{
JSFunction *newfun;
JSObject *callee;
@ -4703,12 +4663,31 @@ BEGIN_CASE(JSOP_APPLY)
goto error;
inlineCallCount++;
JS_RUNTIME_METER(rt, inlineCalls);
TRACE_0(EnterFrame);
CHECK_INTERRUPT_HANDLER();
#ifdef JS_METHODJIT
/* Try to ensure methods are method JIT'd. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp);
if (status == mjit::Compile_Error)
goto error;
if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) {
interpReturnOK = mjit::JaegerShot(cx);
CHECK_INTERRUPT_HANDLER();
goto jit_return;
}
#endif
if (!ScriptPrologue(cx, regs.fp))
goto error;
CHECK_INTERRUPT_HANDLER();
/* Load first op and dispatch it (safe since JSOP_STOP). */
op = (JSOp) *regs.pc;
JS_ASSERT(op == JSOP_BEGIN);
DO_OP();
}
@ -6933,6 +6912,9 @@ END_CASE(JSOP_ARRAYPUSH)
goto inline_return;
exit:
interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
regs.fp->setFinishedInInterpreter();
/*
* At this point we are inevitably leaving an interpreted function or a
* top-level script, and returning to one of:
@ -6945,20 +6927,15 @@ END_CASE(JSOP_ARRAYPUSH)
* frame pc.
*/
JS_ASSERT(entryFrame == regs.fp);
JS_ASSERT(cx->regs == &regs);
*prevContextRegs = regs;
cx->setCurrentRegs(prevContextRegs);
#ifdef JS_TRACER
JS_ASSERT_IF(interpReturnOK && (interpFlags & JSINTERP_RECORD), !TRACE_RECORDER(cx));
JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx));
if (TRACE_RECORDER(cx))
AbortRecording(cx, "recording out of Interpret");
#endif
JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, &regs.fp->scopeChain(), 0));
--cx->interpLevel;
return interpReturnOK;
atom_not_defined:
@ -6971,12 +6948,13 @@ END_CASE(JSOP_ARRAYPUSH)
goto error;
}
/*
* This path is used when it's guaranteed the method can be finished
* inside the JIT.
*/
#if defined(JS_TRACER) && defined(JS_METHODJIT)
stop_recording:
leave_on_safe_point:
#endif
JS_ASSERT(cx->regs == &regs);
*prevContextRegs = regs;
cx->setCurrentRegs(prevContextRegs);
return interpReturnOK;
}

View File

@ -59,10 +59,11 @@ struct JSFrameRegs
};
/* Flags to toggle js::Interpret() execution. */
enum JSInterpFlags
enum JSInterpMode
{
JSINTERP_RECORD = 0x1, /* interpreter has been started to record/run traces */
JSINTERP_SAFEPOINT = 0x2 /* interpreter should leave on a method JIT safe point */
JSINTERP_NORMAL = 0, /* Interpreter is running normally. */
JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */
JSINTERP_SAFEPOINT = 2 /* interpreter should leave on a method JIT safe point */
};
/* Flags used in JSStackFrame::flags_ */
@ -83,7 +84,7 @@ enum JSFrameFlags
/* Temporary frame states */
JSFRAME_ASSIGNING = 0x100, /* not-JOF_ASSIGNING op is assigning */
JSFRAME_YIELDING = 0x200, /* js::Interpret dispatched JSOP_YIELD */
JSFRAME_BAILED_AT_RETURN = 0x400, /* bailed at JSOP_RETURN */
JSFRAME_FINISHED_IN_INTERPRETER = 0x400, /* set if frame finished in Interpret() */
/* Concerning function arguments */
JSFRAME_OVERRIDE_ARGS = 0x1000, /* overridden arguments local variable */
@ -680,12 +681,12 @@ struct JSStackFrame
flags_ &= ~JSFRAME_YIELDING;
}
bool isBailedAtReturn() const {
return flags_ & JSFRAME_BAILED_AT_RETURN;
void setFinishedInInterpreter() {
flags_ |= JSFRAME_FINISHED_IN_INTERPRETER;
}
void setBailedAtReturn() {
flags_ |= JSFRAME_BAILED_AT_RETURN;
bool finishedInInterpreter() const {
return !!(flags_ & JSFRAME_FINISHED_IN_INTERPRETER);
}
/*
@ -982,7 +983,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
* pointed to by cx->fp until completion or error.
*/
extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0);
Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, JSInterpMode mode = JSINTERP_NORMAL);
extern JS_REQUIRES_STACK bool
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp);

View File

@ -1,4 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -111,7 +112,7 @@ JSStackFrame::resetInvokeCallFrame()
JSFRAME_HAS_RVAL |
JSFRAME_HAS_SCOPECHAIN |
JSFRAME_HAS_ANNOTATION |
JSFRAME_BAILED_AT_RETURN)));
JSFRAME_FINISHED_IN_INTERPRETER)));
flags_ &= JSFRAME_FUNCTION |
JSFRAME_OVERFLOW_ARGS |
JSFRAME_HAS_PREVPC |
@ -262,6 +263,11 @@ JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
if (hasCallObj()) {
callObj().setPrivate(this);
otherfp->flags_ &= ~JSFRAME_HAS_CALL_OBJ;
if (js_IsNamedLambda(fun())) {
JSObject *env = callObj().getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
env->setPrivate(this);
}
}
if (hasArgsObj()) {
argsObj().setPrivate(this);
@ -673,6 +679,35 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
return pobj;
}
static inline bool
ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
{
Probes::exitJSFun(cx, fp->maybeFun());
JSInterpreterHook hook = cx->debugHooks->callHook;
if (hook && fp->hasHookData())
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
/*
* An eval frame's parent owns its activation objects. A yielding frame's
* activation objects are transferred to the floating frame, stored in the
* generator.
*/
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
PutActivationObjects(cx, fp);
/*
* If inline-constructing, replace primitive rval with the new object
* passed in via |this|, and instrument this constructor invocation.
*/
if (fp->isConstructing()) {
if (fp->returnValue().isPrimitive())
fp->setReturnValue(ObjectValue(fp->constructorThis()));
JS_RUNTIME_METER(cx->runtime, constructs);
}
return ok;
}
}
#endif /* jsinterpinlines_h__ */

View File

@ -1272,7 +1272,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
if (!cx->ensureGeneratorStackSpace())
return JS_FALSE;
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
switch (op) {
case JSGENOP_NEXT:
case JSGENOP_SEND:

View File

@ -623,6 +623,4 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL
OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT)
/* When adding bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */
/* When changing bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */

View File

@ -308,7 +308,7 @@ GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs
// The method JIT's implementation of instanceof contains an internal lookup
// of the prototype property.
if (op == JSOP_INSTANCEOF || op == JSOP_BEGIN)
if (op == JSOP_INSTANCEOF)
return cx->runtime->atomState.classPrototypeAtom;
ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;

View File

@ -10453,6 +10453,13 @@ TraceRecorder::record_EnterFrame()
RETURN_STOP_A("recursion started inlining");
}
if (fp->isConstructing()) {
LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins };
LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args);
guard(false, lir->insEqP_0(tv_ins), OOM_EXIT);
set(&fp->thisValue(), tv_ins);
}
return ARECORD_CONTINUE;
}
@ -11156,20 +11163,6 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins,
guard(true, lir->insEqI_0(status_ins), STATUS_EXIT);
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_BEGIN()
{
JSStackFrame* fp = cx->fp();
if (fp->isConstructing()) {
LIns* callee_ins = get(&cx->fp()->calleeValue());
LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins };
LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args);
guard(false, lir->insEqP_0(tv_ins), OOM_EXIT);
set(&fp->thisValue(), tv_ins);
}
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK RecordingStatus
TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted)
{

View File

@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 73)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 74)
/*
* Library-private functions.

View File

@ -307,6 +307,12 @@ mjit::Compiler::generatePrologue()
}
}
if (isConstructing)
constructThis();
if (debugMode)
stubCall(stubs::EnterScript);
return Compile_Okay;
}
@ -1343,16 +1349,16 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_LOCALDEC)
BEGIN_CASE(JSOP_BINDNAME)
jsop_bindname(fullAtomIndex(PC));
jsop_bindname(fullAtomIndex(PC), true);
END_CASE(JSOP_BINDNAME)
BEGIN_CASE(JSOP_SETPROP)
jsop_setprop(script->getAtom(fullAtomIndex(PC)));
jsop_setprop(script->getAtom(fullAtomIndex(PC)), true);
END_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETMETHOD)
jsop_setprop(script->getAtom(fullAtomIndex(PC)));
jsop_setprop(script->getAtom(fullAtomIndex(PC)), true);
END_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_THROW)
@ -1702,11 +1708,6 @@ mjit::Compiler::generateMethod()
break;
END_CASE(JSOP_GLOBALINC)
BEGIN_CASE(JSOP_BEGIN)
if (isConstructing)
constructThis();
END_CASE(JSOP_BEGIN)
default:
/* Sorry, this opcode isn't implemented yet. */
#ifdef JS_METHODJIT_SPEW
@ -1930,6 +1931,11 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
/* Only the top of the stack can be returned. */
JS_ASSERT_IF(fe, fe == frame.peek(-1));
if (debugMode) {
prepareStubCall(Uses(0));
stubCall(stubs::LeaveScript);
}
/*
* If there's a function object, deal with the fact that it can escape.
* Note that after we've placed the call object, all tracked state can
@ -2362,20 +2368,28 @@ mjit::Compiler::emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused)
}
void
mjit::Compiler::jsop_setprop_slow(JSAtom *atom)
mjit::Compiler::jsop_setprop_slow(JSAtom *atom, bool usePropCache)
{
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(STRICT_VARIANT(stubs::SetName));
if (usePropCache)
stubCall(STRICT_VARIANT(stubs::SetName));
else
stubCall(STRICT_VARIANT(stubs::SetPropNoCache));
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
frame.shimmy(1);
}
void
mjit::Compiler::jsop_getprop_slow()
mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache)
{
prepareStubCall(Uses(1));
stubCall(stubs::GetProp);
if (usePropCache) {
stubCall(stubs::GetProp);
} else {
masm.move(ImmPtr(atom), Registers::ArgReg1);
stubCall(stubs::GetPropNoCache);
}
frame.pop();
frame.pushSynced();
}
@ -2440,7 +2454,7 @@ mjit::Compiler::passPICAddress(PICGenInfo &pic)
}
void
mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
{
FrameEntry *top = frame.peek(-1);
@ -2448,7 +2462,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) {
JS_ASSERT_IF(atom == cx->runtime->atomState.lengthAtom,
top->getKnownType() != JSVAL_TYPE_STRING);
jsop_getprop_slow();
jsop_getprop_slow(atom, usePropCache);
return;
}
@ -2464,7 +2478,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
shapeReg = frame.allocReg();
}
PICGenInfo pic(ic::PICInfo::GET);
PICGenInfo pic(ic::PICInfo::GET, usePropCache);
/* Guard that the type is an object. */
Jump typeCheck;
@ -2571,7 +2585,7 @@ void
mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID objReg,
RegisterID idReg, RegisterID shapeReg)
{
PICGenInfo pic(ic::PICInfo::GETELEM);
PICGenInfo pic(ic::PICInfo::GETELEM, true);
pic.objRemat = frame.dataRematInfo(obj);
pic.idRemat = frame.dataRematInfo(id);
@ -2684,7 +2698,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
RegisterID objReg = frame.copyDataIntoReg(top);
RegisterID shapeReg = frame.allocReg();
PICGenInfo pic(ic::PICInfo::CALL);
PICGenInfo pic(ic::PICInfo::CALL, true);
/* Guard that the type is an object. */
pic.typeReg = frame.copyTypeIntoReg(top);
@ -2852,7 +2866,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
{
FrameEntry *top = frame.peek(-1);
PICGenInfo pic(ic::PICInfo::CALL);
PICGenInfo pic(ic::PICInfo::CALL, true);
JS_ASSERT(top->isTypeKnown());
JS_ASSERT(top->getKnownType() == JSVAL_TYPE_OBJECT);
@ -2969,20 +2983,20 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
}
void
mjit::Compiler::jsop_setprop(JSAtom *atom)
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
{
FrameEntry *lhs = frame.peek(-2);
FrameEntry *rhs = frame.peek(-1);
/* If the incoming type will never PIC, take slow path. */
if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
jsop_setprop_slow(atom);
jsop_setprop_slow(atom, usePropCache);
return;
}
JSOp op = JSOp(*PC);
PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET);
PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET, usePropCache);
pic.atom = atom;
/* Guard that the type is an object. */
@ -2998,19 +3012,11 @@ mjit::Compiler::jsop_setprop(JSAtom *atom)
pic.typeCheck = stubcc.linkExit(j, Uses(2));
stubcc.leave();
/*
* This gets called from PROPINC/PROPDEC which aren't compatible with
* the normal SETNAME property cache logic.
*/
JSOp op = JSOp(*PC);
stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1);
if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op ==
JSOP_SETMETHOD) {
if (usePropCache)
stubcc.call(STRICT_VARIANT(stubs::SetName));
} else {
else
stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache));
}
typeCheck = stubcc.masm.jump();
pic.hasTypeCheck = true;
} else {
@ -3113,7 +3119,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom)
void
mjit::Compiler::jsop_name(JSAtom *atom)
{
PICGenInfo pic(ic::PICInfo::NAME);
PICGenInfo pic(ic::PICInfo::NAME, true);
pic.shapeReg = frame.allocReg();
pic.objReg = frame.allocReg();
@ -3145,7 +3151,7 @@ mjit::Compiler::jsop_name(JSAtom *atom)
void
mjit::Compiler::jsop_xname(JSAtom *atom)
{
PICGenInfo pic(ic::PICInfo::XNAME);
PICGenInfo pic(ic::PICInfo::XNAME, true);
FrameEntry *fe = frame.peek(-1);
if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
@ -3187,9 +3193,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
}
void
mjit::Compiler::jsop_bindname(uint32 index)
mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
{
PICGenInfo pic(ic::PICInfo::BIND);
PICGenInfo pic(ic::PICInfo::BIND, usePropCache);
pic.shapeReg = frame.allocReg();
pic.objReg = frame.allocReg();
@ -3250,9 +3256,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
}
void
mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck)
mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck, bool usePropCache)
{
jsop_getprop_slow();
jsop_getprop_slow(atom, usePropCache);
}
bool
@ -3262,13 +3268,13 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
}
void
mjit::Compiler::jsop_setprop(JSAtom *atom)
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
{
jsop_setprop_slow(atom);
jsop_setprop_slow(atom, usePropCache);
}
void
mjit::Compiler::jsop_bindname(uint32 index)
mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
{
RegisterID reg = frame.allocReg();
Address scopeChain(JSFrameReg, JSStackFrame::offsetOfScopeChain());
@ -3280,7 +3286,12 @@ mjit::Compiler::jsop_bindname(uint32 index)
stubcc.linkExit(j, Uses(0));
stubcc.leave();
stubcc.call(stubs::BindName);
if (usePropCache) {
stubcc.call(stubs::BindName);
} else {
masm.move(ImmPtr(script->getAtom(index)), Registers::ArgReg1);
stubcc.call(stubs::BindNameNoCache);
}
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
@ -3423,7 +3434,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
jsop_binary(JSOP_SUB, stubs::Sub);
// N+1
jsop_bindname(index);
jsop_bindname(index, false);
// V+1 OBJ
frame.dup2();
@ -3435,7 +3446,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.shift(-1);
// OBJ V+1
jsop_setprop(atom);
jsop_setprop(atom, false);
// V+1
if (pop)
@ -3458,7 +3469,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
jsop_binary(JSOP_ADD, stubs::Add);
// N N+1
jsop_bindname(index);
jsop_bindname(index, false);
// N N+1 OBJ
frame.dup2();
@ -3470,7 +3481,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.shift(-1);
// N OBJ N+1
jsop_setprop(atom);
jsop_setprop(atom, false);
// N N+1
frame.pop();
@ -3516,7 +3527,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
jsop_binary(JSOP_SUB, stubs::Sub);
// OBJ V+1
jsop_setprop(atom);
jsop_setprop(atom, false);
// V+1
if (pop)
@ -3548,7 +3559,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
frame.dupAt(-2);
// OBJ N N+1 OBJ N+1
jsop_setprop(atom);
jsop_setprop(atom, false);
// OBJ N N+1 N+1
frame.popn(2);
@ -4353,7 +4364,7 @@ mjit::Compiler::constructThis()
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg);
// Get callee.prototype.
jsop_getprop(cx->runtime->atomState.classPrototypeAtom);
jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false, false);
// Reach into the proto Value and grab a register for its data.
FrameEntry *protoFe = frame.peek(-1);

View File

@ -139,7 +139,8 @@ class Compiler : public BaseCompiler
#if defined JS_POLYIC
struct PICGenInfo {
PICGenInfo(ic::PICInfo::Kind kind) : kind(kind)
PICGenInfo(ic::PICInfo::Kind kind, bool usePropCache)
: kind(kind), usePropCache(usePropCache)
{ }
ic::PICInfo::Kind kind;
Label fastPathStart;
@ -157,23 +158,25 @@ class Compiler : public BaseCompiler
StateRemat idRemat;
Call callReturn;
bool hasTypeCheck;
bool usePropCache;
ValueRemat vr;
# if defined JS_CPU_X64
ic::PICLabels labels;
# endif
void copySimpleMembersTo(ic::PICInfo &pi) const {
pi.kind = kind;
pi.shapeReg = shapeReg;
pi.objReg = objReg;
pi.atom = atom;
void copySimpleMembersTo(ic::PICInfo &ic) const {
ic.kind = kind;
ic.shapeReg = shapeReg;
ic.objReg = objReg;
ic.atom = atom;
ic.usePropCache = usePropCache;
if (kind == ic::PICInfo::SET) {
pi.u.vr = vr;
ic.u.vr = vr;
} else if (kind != ic::PICInfo::NAME) {
pi.u.get.idReg = idReg;
pi.u.get.typeReg = typeReg;
pi.u.get.hasTypeCheck = hasTypeCheck;
pi.u.get.objRemat = objRemat.offset;
ic.u.get.idReg = idReg;
ic.u.get.typeReg = typeReg;
ic.u.get.hasTypeCheck = hasTypeCheck;
ic.u.get.objRemat = objRemat.offset;
}
}
@ -275,10 +278,10 @@ class Compiler : public BaseCompiler
/* Opcode handlers. */
void jumpAndTrace(Jump j, jsbytecode *target, Jump *slowOne = NULL, Jump *slowTwo = NULL);
void jsop_bindname(uint32 index);
void jsop_bindname(uint32 index, bool usePropCache);
void jsop_setglobal(uint32 index);
void jsop_getglobal(uint32 index);
void jsop_getprop_slow();
void jsop_getprop_slow(JSAtom *atom, bool usePropCache = true);
void jsop_getarg(uint32 index);
void jsop_this();
void emitReturn(FrameEntry *fe);
@ -302,10 +305,10 @@ class Compiler : public BaseCompiler
void jsop_setelem_slow();
void jsop_getelem_slow();
void jsop_unbrand();
void jsop_getprop(JSAtom *atom, bool typeCheck = true);
void jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true);
void jsop_length();
void jsop_setprop(JSAtom *atom);
void jsop_setprop_slow(JSAtom *atom);
void jsop_setprop(JSAtom *atom, bool usePropCache = true);
void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);
bool jsop_callprop_slow(JSAtom *atom);
bool jsop_callprop(JSAtom *atom);
bool jsop_callprop_obj(JSAtom *atom);

View File

@ -73,9 +73,6 @@ using namespace js;
using namespace js::mjit;
using namespace JSC;
static bool
InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame = JS_TRUE);
static jsbytecode *
FindExceptionHandler(JSContext *cx)
{
@ -177,8 +174,8 @@ top:
* either because of a call into an un-JITable script, or because the call is
* throwing an exception.
*/
static bool
InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame)
static void
InlineReturn(VMFrame &f)
{
JSContext *cx = f.cx;
JSStackFrame *fp = f.regs.fp;
@ -187,36 +184,9 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame)
JS_ASSERT(!js_IsActiveWithOrBlock(cx, &fp->scopeChain(), 0));
// Marker for debug support.
if (JS_UNLIKELY(fp->hasHookData())) {
JSInterpreterHook hook;
JSBool status;
hook = cx->debugHooks->callHook;
if (hook) {
/*
* Do not pass &ok directly as exposing the address inhibits
* optimizations and uninitialised warnings.
*/
status = ok;
hook(cx, fp, JS_FALSE, &status, fp->hookData());
ok = (status == JS_TRUE);
// CHECK_INTERRUPT_HANDLER();
}
}
PutActivationObjects(cx, fp);
if (fp->isConstructing() && fp->returnValue().isPrimitive())
fp->setReturnValue(fp->thisValue());
if (popFrame) {
Value *newsp = fp->actualArgs() - 1;
newsp[-1] = fp->returnValue();
cx->stack().popInlineFrame(cx, fp->prev(), newsp);
}
return ok;
Value *newsp = fp->actualArgs() - 1;
newsp[-1] = fp->returnValue();
cx->stack().popInlineFrame(cx, fp->prev(), newsp);
}
void JS_FASTCALL
@ -372,7 +342,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
/* Function did not compile... interpret it. */
JSBool ok = Interpret(cx, fp);
InlineReturn(f, ok);
InlineReturn(f);
if (!ok)
THROWV(NULL);
@ -384,7 +354,6 @@ static inline bool
UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
{
JSContext *cx = f.cx;
JSStackFrame *fp = f.fp();
Value *vp = f.regs.sp - (argc + 2);
JSObject &callee = vp->toObject();
JSFunction *newfun = callee.getFunctionPrivate();
@ -412,17 +381,11 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp))
return false;
/* Marker for debug support. */
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
newfp->setHookData(hook(cx, fp, JS_TRUE, 0,
cx->debugHooks->callHookData));
}
/* Try to compile if not already compiled. */
if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
if (mjit::TryCompile(cx, newfp) == Compile_Error) {
/* A runtime exception was thrown, get out. */
InlineReturn(f, JS_FALSE);
InlineReturn(f);
return false;
}
}
@ -435,7 +398,7 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
/* Otherwise, run newscript in the interpreter. */
bool ok = !!Interpret(cx, cx->fp());
InlineReturn(f, JS_TRUE);
InlineReturn(f);
*pret = NULL;
return ok;
@ -574,11 +537,18 @@ js_InternalThrow(VMFrame &f)
// JS function.
bool lastFrame = (f.entryFp == f.fp());
js_UnwindScope(cx, 0, cx->throwing);
// For consistency with Interpret(), always run the script epilogue.
// This simplifies interactions with RunTracer(), since it can assume
// no matter how a function exited (error or not), that the epilogue
// does not need to be run.
ScriptEpilogue(f.cx, f.fp(), false);
if (lastFrame)
break;
JS_ASSERT(f.regs.sp == cx->regs->sp);
InlineReturn(f, JS_FALSE);
InlineReturn(f);
}
JS_ASSERT(f.regs.sp == cx->regs->sp);
@ -611,21 +581,43 @@ stubs::CreateThis(VMFrame &f, JSObject *proto)
fp->formalArgs()[-1].setObject(*obj);
}
static inline void
AdvanceReturnPC(JSContext *cx)
void JS_FASTCALL
stubs::EnterScript(VMFrame &f)
{
/* Simulate an inline_return by advancing the pc. */
JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
*cx->regs->pc == JSOP_NEW ||
*cx->regs->pc == JSOP_EVAL ||
*cx->regs->pc == JSOP_APPLY);
cx->regs->pc += JSOP_CALL_LENGTH;
JSStackFrame *fp = f.fp();
JSContext *cx = f.cx;
JSInterpreterHook hook = cx->debugHooks->callHook;
if (JS_UNLIKELY(hook != NULL))
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
Probes::enterJSFun(cx, fp->maybeFun());
}
void JS_FASTCALL
stubs::LeaveScript(VMFrame &f)
{
JSStackFrame *fp = f.fp();
JSContext *cx = f.cx;
Probes::exitJSFun(cx, fp->maybeFun());
JSInterpreterHook hook = cx->debugHooks->callHook;
if (hook && fp->hasHookData()) {
JSBool ok = JS_TRUE;
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
if (!ok)
THROW();
}
}
#ifdef JS_TRACER
/*
* Called when an error is in progress and the topmost frame could not handle
* it. This will unwind to a given frame, or find and align to an exception
* handler in the process.
*/
static inline bool
HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true)
{
JSContext *cx = f.cx;
@ -633,14 +625,19 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
* Callers of this called either Interpret() or JaegerShot(), which would
* have searched for exception handlers already. If we see stopFp, just
* return false. Otherwise, pop the frame, since it's guaranteed useless.
*
* Note that this also guarantees ScriptEpilogue() has been called.
*/
JSStackFrame *fp = cx->fp();
if (fp == stopFp)
return false;
if (searchedTopmostFrame) {
if (fp == stopFp)
return false;
bool returnOK = InlineReturn(f, false);
InlineReturn(f);
}
/* Remove the bottom frame. */
bool returnOK = false;
for (;;) {
fp = cx->fp();
@ -667,7 +664,8 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
/* Unwind and return. */
returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->throwing));
returnOK = InlineReturn(f, returnOK);
returnOK = ScriptEpilogue(cx, fp, returnOK);
InlineReturn(f);
}
JS_ASSERT(&f.regs == cx->regs);
@ -676,6 +674,7 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
return returnOK;
}
/* Returns whether the current PC has method JIT'd code. */
static inline void *
AtSafePoint(JSContext *cx)
{
@ -687,6 +686,10 @@ AtSafePoint(JSContext *cx)
return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc);
}
/*
* Interprets until either a safe point is reached that has method JIT'd
* code, or the current frame tries to return.
*/
static inline JSBool
PartialInterpret(VMFrame &f)
{
@ -695,6 +698,7 @@ PartialInterpret(VMFrame &f)
#ifdef DEBUG
JSScript *script = fp->script();
JS_ASSERT(!fp->finishedInInterpreter());
JS_ASSERT(fp->hasImacropc() ||
!script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc));
#endif
@ -707,6 +711,7 @@ PartialInterpret(VMFrame &f)
JS_STATIC_ASSERT(JSOP_NOP == 0);
/* Returns whether the current PC would return, popping the frame. */
static inline JSOp
FrameIsFinished(JSContext *cx)
{
@ -718,39 +723,134 @@ FrameIsFinished(JSContext *cx)
: JSOP_NOP;
}
/* Simulate an inline_return by advancing the pc. */
static inline void
AdvanceReturnPC(JSContext *cx)
{
JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
*cx->regs->pc == JSOP_NEW ||
*cx->regs->pc == JSOP_EVAL ||
*cx->regs->pc == JSOP_APPLY);
cx->regs->pc += JSOP_CALL_LENGTH;
}
/*
* Given a frame that is about to return, make sure its return value and
* activation objects are fixed up. Then, pop the frame and advance the
* current PC. Note that while we could enter the JIT at this point, the
* logic would still be necessary for the interpreter, so it's easier
* (and faster) to finish frames in C++ even if at a safe point here.
*/
static bool
HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame)
{
JSContext *cx = f.cx;
JS_ASSERT(FrameIsFinished(cx));
/*
* This is the most difficult and complicated piece of the tracer
* integration, and historically has been very buggy. The problem is that
* although this frame has to be popped (see RemoveExcessFrames), it may
* be at a JSOP_RETURN opcode, and it might not have ever been executed.
* That is, fp->rval may not be set to the top of the stack, and if it
* has, the stack has already been decremented. Note that fp->rval is not
* the only problem: the epilogue may never have been executed.
*
* Here are the edge cases and whether the frame has been exited cleanly:
* 1. No: A trace exited directly before a RETURN op, and the
* interpreter never ran.
* 2. Yes: The interpreter exited cleanly.
* 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT
* is not used in between JSOP_RETURN and advancing the PC,
* therefore, it cannot have been run if at a safe point.
* 4. No: Somewhere in the RunTracer call tree, we removed a frame,
* and we returned to a JSOP_RETURN opcode. Note carefully
* that in this situation, FrameIsFinished() returns true!
* 5. Yes: The function exited in the method JIT. However, in this
* case, we'll never enter HandleFinishedFrame(): we always
* immediately pop JIT'd frames.
*
* Since the only scenario where this fixup is NOT needed is a normal exit
* from the interpreter, we can cleanly check for this scenario by checking
* a bit it sets in the frame.
*/
bool returnOK = true;
if (!cx->fp()->finishedInInterpreter()) {
if (JSOp(*cx->regs->pc) == JSOP_RETURN)
cx->fp()->setReturnValue(f.regs.sp[-1]);
returnOK = ScriptEpilogue(cx, cx->fp(), true);
}
JS_ASSERT_IF(cx->fp()->isFunctionFrame() &&
!cx->fp()->isEvalFrame(),
!cx->fp()->hasCallObj());
if (cx->fp() != entryFrame) {
InlineReturn(f);
AdvanceReturnPC(cx);
}
return returnOK;
}
/*
* Given a frame newer than the entry frame, try to finish it. If it's at a
* return position, pop the frame. If it's at a safe point, execute it in
* Jaeger code. Otherwise, try to interpret until a safe point.
*
* While this function is guaranteed to make progress, it may not actually
* finish or pop the current frame. It can either:
* 1) Finalize a finished frame, or
* 2) Finish and finalize the frame in the Method JIT, or
* 3) Interpret, which can:
* a) Propagate an error, or
* b) Finish the frame, but not finalize it, or
* c) Abruptly leave at any point in the frame, or in a newer frame
* pushed by a call, that has method JIT'd code.
*/
static bool
EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame)
{
JSContext *cx = f.cx;
JSStackFrame *fp = cx->fp();
/*
* A "finished" frame is when the interpreter rested on a STOP,
* RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
* for a safe point. If the frame was finished, we could have already
* called ScriptEpilogue(), and entering the JIT could call it twice.
*/
if (!fp->hasImacropc() && FrameIsFinished(cx))
return HandleFinishedFrame(f, entryFrame);
if (void *ncode = AtSafePoint(cx)) {
if (!JaegerShotAtSafePoint(cx, ncode))
return false;
InlineReturn(f);
AdvanceReturnPC(cx);
return true;
}
return PartialInterpret(f);
}
/*
* Evaluate frames newer than the entry frame until all are gone. This will
* always leave f.regs.fp == entryFrame.
*/
static bool
FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
{
JSContext *cx = f.cx;
while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
if (void *ncode = AtSafePoint(cx)) {
if (!JaegerShotAtSafePoint(cx, ncode)) {
if (!HandleErrorInExcessFrames(f, entryFrame))
return false;
/* Could be anywhere - restart outer loop. */
continue;
}
InlineReturn(f, JS_TRUE);
AdvanceReturnPC(cx);
} else {
if (!PartialInterpret(f)) {
if (!HandleErrorInExcessFrames(f, entryFrame))
return false;
} else if (cx->fp() != entryFrame) {
/*
* Partial interpret could have dropped us anywhere. Deduce the
* edge case: at a RETURN, needing to pop a frame.
*/
JS_ASSERT(!cx->fp()->hasImacropc());
if (FrameIsFinished(cx)) {
JSOp op = JSOp(*cx->regs->pc);
if (op == JSOP_RETURN && !cx->fp()->isBailedAtReturn())
cx->fp()->setReturnValue(f.regs.sp[-1]);
InlineReturn(f, JS_TRUE);
AdvanceReturnPC(cx);
}
}
while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
if (!EvaluateExcessFrame(f, entryFrame)) {
if (!HandleErrorInExcessFrame(f, entryFrame))
return false;
}
}
@ -825,18 +925,18 @@ RunTracer(VMFrame &f)
DisableTraceHint(f, mic);
#endif
if ((tpa == TPA_RanStuff || tpa == TPA_Recorded) && cx->throwing)
tpa = TPA_Error;
// Even though ExecuteTree() bypasses the interpreter, it should propagate
// error failures correctly.
JS_ASSERT_IF(cx->throwing, tpa == TPA_Error);
/* Sync up the VMFrame's view of cx->fp(). */
f.fp() = cx->fp();
JS_ASSERT(f.fp() == cx->fp());
switch (tpa) {
case TPA_Nothing:
return NULL;
case TPA_Error:
if (!HandleErrorInExcessFrames(f, entryFrame))
if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
THROWV(NULL);
JS_ASSERT(!cx->fp()->hasImacropc());
break;
@ -874,32 +974,26 @@ RunTracer(VMFrame &f)
THROWV(NULL);
/* IMacros are guaranteed to have been removed by now. */
JS_ASSERT(f.fp() == entryFrame);
JS_ASSERT(!entryFrame->hasImacropc());
/* Step 2. If entryFrame is at a safe point, just leave. */
if (void *ncode = AtSafePoint(cx))
return ncode;
/* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */
if (JSOp op = FrameIsFinished(cx)) {
/* We're not guaranteed that the RETURN was run. */
if (op == JSOP_RETURN && !entryFrame->isBailedAtReturn())
entryFrame->setReturnValue(f.regs.sp[-1]);
/* Cleanup activation objects on the frame unless it's owned by an Invoke. */
if (f.fp() != f.entryFp) {
if (!InlineReturn(f, JS_TRUE, JS_FALSE))
THROWV(NULL);
}
/* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
if (FrameIsFinished(cx)) {
if (!HandleFinishedFrame(f, entryFrame))
THROWV(NULL);
void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
*f.returnAddressLocation() = retPtr;
return NULL;
}
/* Step 3. If entryFrame is at a safe point, just leave. */
if (void *ncode = AtSafePoint(cx))
return ncode;
/* Step 4. Do a partial interp, then restart the whole process. */
if (!PartialInterpret(f)) {
if (!HandleErrorInExcessFrames(f, entryFrame))
if (!HandleErrorInExcessFrame(f, entryFrame))
THROWV(NULL);
}

View File

@ -690,10 +690,10 @@ class SetPropCompiler : public PICStubCompiler
class GetPropCompiler : public PICStubCompiler
{
JSObject *obj;
JSAtom *atom;
void *stub;
int lastStubSecondShapeGuard;
JSObject *obj;
JSAtom *atom;
VoidStubPIC stub;
int lastStubSecondShapeGuard;
static int32 inlineShapeOffset(ic::PICInfo &pic) {
#if defined JS_NUNBOX32
@ -732,17 +732,12 @@ class GetPropCompiler : public PICStubCompiler
}
public:
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
VoidStub stub)
: PICStubCompiler("getprop", f, script, pic), obj(obj), atom(atom),
stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
VoidStubPIC stub)
: PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom),
stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
: PICStubCompiler(pic.kind == ic::PICInfo::CALL ? "callprop" : "getprop", f, script, pic),
obj(obj),
atom(atom),
stub(stub),
lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
@ -2012,6 +2007,24 @@ class BindNameCompiler : public PICStubCompiler
}
};
static void JS_FASTCALL
DisabledLengthIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::Length(f);
}
static void JS_FASTCALL
DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetProp(f);
}
static void JS_FASTCALL
DisabledGetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetPropNoCache(f, pic->atom);
}
void JS_FASTCALL
ic::GetProp(VMFrame &f, ic::PICInfo *pic)
{
@ -2020,7 +2033,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
JSAtom *atom = pic->atom;
if (atom == f.cx->runtime->atomState.lengthAtom) {
if (f.regs.sp[-1].isString()) {
GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length);
GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledLengthIC);
if (!cc.generateStringLengthStub()) {
cc.disable("error");
THROW();
@ -2031,7 +2044,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
} else if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) {
GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length);
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
if (obj->isArray()) {
if (!cc.generateArrayLengthStub()) {
cc.disable("error");
@ -2056,7 +2069,10 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
THROW();
if (pic->shouldGenerate()) {
GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp);
VoidStubPIC stub = pic->usePropCache
? DisabledGetPropIC
: DisabledGetPropICNoCache;
GetPropCompiler cc(f, script, obj, *pic, atom, stub);
if (!cc.update()) {
cc.disable("error");
THROW();
@ -2100,16 +2116,16 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic)
template <JSBool strict>
static void JS_FASTCALL
SetPropDumb(VMFrame &f, ic::PICInfo *pic)
DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::SetPropNoCache<strict>(f, pic->atom);
stubs::SetName<strict>(f, pic->atom);
}
template <JSBool strict>
static void JS_FASTCALL
SetPropSlow(VMFrame &f, ic::PICInfo *pic)
DisabledSetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::SetName<strict>(f, pic->atom);
stubs::SetPropNoCache<strict>(f, pic->atom);
}
void JS_FASTCALL
@ -2131,22 +2147,9 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
// cache can't handle a GET and SET from the same scripted PC.
//
VoidStubPIC stub;
switch (JSOp(*f.regs.pc)) {
case JSOP_PROPINC:
case JSOP_PROPDEC:
case JSOP_INCPROP:
case JSOP_DECPROP:
case JSOP_NAMEINC:
case JSOP_NAMEDEC:
case JSOP_INCNAME:
case JSOP_DECNAME:
stub = STRICT_VARIANT(SetPropDumb);
break;
default:
stub = STRICT_VARIANT(SetPropSlow);
break;
}
VoidStubPIC stub = pic->usePropCache
? STRICT_VARIANT(DisabledSetPropIC)
: STRICT_VARIANT(DisabledSetPropICNoCache);
SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub);
if (!cc.update()) {
@ -2159,7 +2162,7 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
}
static void JS_FASTCALL
CallPropSlow(VMFrame &f, ic::PICInfo *pic)
DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::CallProp(f, pic->atom);
}
@ -2254,7 +2257,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
}
}
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow);
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC);
if (usePIC) {
if (lval.isObject()) {
if (!cc.update()) {
@ -2283,13 +2286,13 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
}
static void JS_FASTCALL
SlowName(VMFrame &f, ic::PICInfo *pic)
DisabledNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::Name(f);
}
static void JS_FASTCALL
SlowXName(VMFrame &f, ic::PICInfo *pic)
DisabledXNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetProp(f);
}
@ -2302,7 +2305,7 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
/* GETXPROP is guaranteed to have an object. */
JSObject *obj = &f.regs.sp[-1].toObject();
ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName);
ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, DisabledXNameIC);
if (!cc.updateForXName()) {
cc.disable("error");
@ -2320,7 +2323,7 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName);
ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, DisabledNameIC);
if (!cc.updateForName()) {
cc.disable("error");
@ -2334,17 +2337,26 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
}
static void JS_FASTCALL
SlowBindName(VMFrame &f, ic::PICInfo *pic)
DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::BindName(f);
}
static void JS_FASTCALL
DisabledBindNameICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::BindNameNoCache(f, pic->atom);
}
void JS_FASTCALL
ic::BindName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName);
VoidStubPIC stub = pic->usePropCache
? DisabledBindNameIC
: DisabledBindNameICNoCache;
BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, stub);
JSObject *obj = cc.update();
if (!obj) {

View File

@ -236,6 +236,9 @@ struct PICInfo {
// last stub.
bool shapeRegHasBaseShape : 1;
// True if can use the property cache.
bool usePropCache : 1;
// State flags.
bool hit : 1; // this PIC has been executed
bool inlinePathPatched : 1; // inline path has been patched

View File

@ -99,6 +99,15 @@ stubs::BindName(VMFrame &f)
f.regs.sp[-1].setObject(*obj);
}
void JS_FASTCALL
stubs::BindNameNoCache(VMFrame &f, JSAtom *atom)
{
JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom));
if (!obj)
THROW();
f.regs.sp[0].setObject(*obj);
}
JSObject * JS_FASTCALL
stubs::BindGlobalName(VMFrame &f)
{
@ -2068,6 +2077,20 @@ stubs::GetProp(VMFrame &f)
THROW();
}
void JS_FASTCALL
stubs::GetPropNoCache(VMFrame &f, JSAtom *atom)
{
JSContext *cx = f.cx;
Value *vp = &f.regs.sp[-1];
JSObject *obj = ValueToObject(cx, vp);
if (!obj)
THROW();
if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp))
THROW();
}
void JS_FASTCALL
stubs::CallProp(VMFrame &f, JSAtom *origAtom)
{

View File

@ -65,6 +65,8 @@ 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 EnterScript(VMFrame &f);
void JS_FASTCALL LeaveScript(VMFrame &f);
/*
* Result struct for UncachedXHelper.
@ -112,6 +114,7 @@ void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
void JS_FASTCALL BindName(VMFrame &f);
void JS_FASTCALL BindNameNoCache(VMFrame &f, JSAtom *atom);
JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);
template<JSBool strict> void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom);
template<JSBool strict> void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom);
@ -119,6 +122,7 @@ template<JSBool strict> void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom)
template<JSBool strict> void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom);
void JS_FASTCALL Name(VMFrame &f);
void JS_FASTCALL GetProp(VMFrame &f);
void JS_FASTCALL GetPropNoCache(VMFrame &f, JSAtom *atom);
void JS_FASTCALL GetElem(VMFrame &f);
void JS_FASTCALL CallElem(VMFrame &f);
template<JSBool strict> void JS_FASTCALL SetElem(VMFrame &f);

View File

@ -3,5 +3,5 @@ function main() {
return "failure";
}
/* JSOP_RETURN in main. */
trap(main, 4, "'success'");
trap(main, 3, "'success'");
assertEq(main(), "success");

View File

@ -10,8 +10,8 @@ function child() {
function parent() {
x = "failure2";
}
/* First op in parent: because of JSOP_BEGIN, it is op 1. */
trap(parent, 1, "child()");
/* First op in parent. */
trap(parent, 0, "child()");
function success() {
x = "success";

View File

@ -6,14 +6,14 @@ function doNothing() { }
function myparent(nested) {
if (nested) {
/* JSOP_CALL to doNothing in myparent with nested = true. */
trap(myparent, 25, "success()");
trap(myparent, 24, "success()");
doNothing();
} else {
doNothing();
}
}
/* JSOP_CALL to doNothing in myparent with nested = false. */
trap(myparent, 36, "myparent(true)");
trap(myparent, 35, "myparent(true)");
function success() {
x = "success";

View File

@ -0,0 +1,25 @@
var o = { };
for (var i = 0; i <= 50; i++)
o[i] = i;
Object.defineProperty(o, "51", { get: assertEq });
var threw = 0;
function g(o, i) {
try {
assertEq(o[i], i);
} catch (e) {
threw++;
}
}
function f() {
for (var i = 0; i <= 51; i++)
g(o, i);
}
f();
f();
f();
assertEq(threw, 3);