--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -237,18 +237,18 @@ js_FillPropertyCache(JSContext *cx, JSOb
vword = JSVAL_OBJECT_TO_PCVAL(v);
break;
}
}
}
/* If getting a value via a stub getter, we can cache the slot. */
if (!(cs->format & JOF_SET) &&
- !((cs->format & (JOF_INCDEC | JOF_FOR)) &&
- (sprop->attrs & JSPROP_READONLY)) &&
+ !((cs->format & (JOF_INCDEC | JOF_FOR)) &&
+ (sprop->attrs & JSPROP_READONLY)) &&
SPROP_HAS_STUB_GETTER(sprop) &&
SPROP_HAS_VALID_SLOT(sprop, scope)) {
/* Great, let's cache sprop's slot and use it on cache hit. */
vword = SLOT_TO_PCVAL(sprop->slot);
} else {
/* Best we can do is to cache sprop (still a nice speedup). */
vword = SPROP_TO_PCVAL(sprop);
}
@@ -2411,22 +2411,16 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(IN
__SUNPRO_C >= 0x570)
# define JS_THREADED_INTERP 1
# else
# define JS_THREADED_INTERP 0
# endif
#endif
/*
- * Interpreter assumes the following to implement condition-free interrupt
- * implementation when !JS_THREADED_INTERP.
- */
-JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);
-
-/*
* Ensure that the intrepreter switch can close call-bytecode cases in the
* same way as non-call bytecodes.
*/
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
@@ -2472,22 +2466,16 @@ js_Interpret(JSContext *cx)
JSProperty *prop;
JSScopeProperty *sprop;
JSString *str, *str2;
jsint i, j;
jsdouble d, d2;
JSClass *clasp;
JSFunction *fun;
JSType type;
-#if JS_THREADED_INTERP
- register void * const *jumpTable;
-#else
- register uint32 switchMask;
- uintN switchOp;
-#endif
jsint low, high, off, npairs;
JSBool match;
#if JS_HAS_GETTER_SETTER
JSPropertyOp getter, setter;
#endif
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
#ifdef __GNUC__
@@ -2501,73 +2489,77 @@ js_Interpret(JSContext *cx)
#if JS_THREADED_INTERP
static void *const normalJumpTable[] = {
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
JS_EXTENSION &&L_##op,
# include "jsopcode.tbl"
# undef OPDEF
};
-#ifdef JS_TRACER
- static void *const recordingJumpTable[] = {
-# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
- JS_EXTENSION &&R_##op,
+ static void *const interruptJumpTable[] = {
+# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
+ JS_EXTENSION &&interrupt,
# include "jsopcode.tbl"
# undef OPDEF
};
-#endif /* JS_TRACER */
-
- static void *const interruptJumpTable[] = {
-# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
- JS_EXTENSION &&L_JSOP_INTERRUPT,
-# include "jsopcode.tbl"
-# undef OPDEF
- };
+
+ register void * const *jumpTable = normalJumpTable;
METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
+# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
+
# ifdef JS_TRACER
-# define CHECK_RECORDER() JS_BEGIN_MACRO \
- JS_ASSERT(!TRACE_RECORDER(cx) ^ \
- (jumpTable == recordingJumpTable)); \
- JS_END_MACRO
+# define CHECK_RECORDER() \
+ JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
# else
# define CHECK_RECORDER() ((void)0)
# endif
# define DO_OP() JS_BEGIN_MACRO \
CHECK_RECORDER(); \
JS_EXTENSION_(goto *jumpTable[op]); \
JS_END_MACRO
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \
METER_OP_PAIR(op, regs.pc[n]); \
op = (JSOp) *(regs.pc += (n)); \
DO_OP(); \
JS_END_MACRO
-# define BEGIN_CASE(OP) L_##OP: \
- CHECK_RECORDER();
+# define BEGIN_CASE(OP) L_##OP: CHECK_RECORDER();
# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
# define END_VARLEN_CASE DO_NEXT_OP(len);
# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \
JS_ASSERT(js_CodeSpec[OP].length == 1); \
op = (JSOp) *++regs.pc; \
DO_OP();
# define END_EMPTY_CASES
#else /* !JS_THREADED_INTERP */
+ register intN switchMask = 0;
+ intN switchOp;
+
+# define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
+
+# ifdef JS_TRACER
+# define CHECK_RECORDER() \
+ JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
+# else
+# define CHECK_RECORDER() ((void)0)
+# endif
+
# define DO_OP() goto do_op
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \
JS_ASSERT((n) == len); \
goto advance_pc; \
JS_END_MACRO
-# define BEGIN_CASE(OP) case OP:
+# define BEGIN_CASE(OP) case OP: CHECK_RECORDER();
# define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
# define END_CASE_LEN(n) END_CASE_LENX(n)
# define END_CASE_LENX(n) END_CASE_LEN##n
/*
* To share the code for all len == 1 cases we use the specialized label with
* code that falls through to advance_pc: .
*/
@@ -2644,17 +2636,20 @@ js_Interpret(JSContext *cx)
#define LOAD_FUNCTION(PCOFF) \
JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
#ifdef JS_TRACER
#define MONITOR_BRANCH() \
JS_BEGIN_MACRO \
if (TRACING_ENABLED(cx)) { \
- ENABLE_TRACER(js_MonitorLoopEdge(cx, inlineCallCount)); \
+ if (js_MonitorLoopEdge(cx, inlineCallCount)) { \
+ JS_ASSERT(TRACE_RECORDER(cx)); \
+ ENABLE_INTERRUPTS(); \
+ } \
fp = cx->fp; \
script = fp->script; \
atoms = fp->imacpc \
? COMMON_ATOMS_START(&rt->atomState) \
: script->atomMap.vector; \
currentVersion = (JSVersion) script->version; \
JS_ASSERT(fp->regs == ®s); \
if (cx->throwing) \
@@ -2713,63 +2708,29 @@ js_Interpret(JSContext *cx)
JSStackFrame **disp = &cx->display[script->staticDepth];
fp->displaySave = *disp;
*disp = fp;
}
#ifdef DEBUG
fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
#endif
+# define CHECK_INTERRUPT_HANDLER() \
+ JS_BEGIN_MACRO \
+ if (cx->debugHooks->interruptHandler) \
+ ENABLE_INTERRUPTS(); \
+ JS_END_MACRO
+
/*
* Load the debugger's interrupt hook here and after calling out to native
* functions (but not to getters, setters, or other native hooks), so we do
* not have to reload it each time through the interpreter loop -- we hope
* the compiler can keep it in a register when it is non-null.
*/
-#if JS_THREADED_INTERP
-#ifdef JS_TRACER
-# define LOAD_INTERRUPT_HANDLER(cx) \
- ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
- ? interruptJumpTable \
- : TRACE_RECORDER(cx) \
- ? recordingJumpTable \
- : normalJumpTable))
-# define ENABLE_TRACER(flag) \
- JS_BEGIN_MACRO \
- bool flag_ = (flag); \
- JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
- jumpTable = flag_ ? recordingJumpTable : normalJumpTable; \
- JS_END_MACRO
-#else /* !JS_TRACER */
-# define LOAD_INTERRUPT_HANDLER(cx) \
- ((void) (jumpTable = (cx)->debugHooks->interruptHandler \
- ? interruptJumpTable \
- : normalJumpTable))
-# define ENABLE_TRACER(flag) ((void)0)
-#endif /* !JS_TRACER */
-#else /* !JS_THREADED_INTERP */
-#ifdef JS_TRACER
-# define LOAD_INTERRUPT_HANDLER(cx) \
- ((void) (switchMask = ((cx)->debugHooks->interruptHandler || \
- TRACE_RECORDER(cx)) ? 0 : 255))
-# define ENABLE_TRACER(flag) \
- JS_BEGIN_MACRO \
- bool flag_ = (flag); \
- JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
- switchMask = flag_ ? 0 : 255; \
- JS_END_MACRO
-#else /* !JS_TRACER */
-# define LOAD_INTERRUPT_HANDLER(cx) \
- ((void) (switchMask = ((cx)->debugHooks->interruptHandler \
- ? 0 : 255)))
-# define ENABLE_TRACER(flag) ((void)0)
-#endif /* !JS_TRACER */
-#endif /* !JS_THREADED_INTERP */
-
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
#if !JS_HAS_GENERATORS
JS_ASSERT(!fp->regs);
#else
/* Initialize the pc and sp registers unless we're resuming a generator. */
if (JS_LIKELY(!fp->regs)) {
#endif
ASSERT_NOT_THROWING(cx);
@@ -2815,75 +2776,91 @@ js_Interpret(JSContext *cx)
len = 0;
DO_NEXT_OP(len);
#if JS_THREADED_INTERP
/*
* This is a loop, but it does not look like a loop. The loop-closing
* jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
* When interrupts are enabled, jumpTable is set to interruptJumpTable
- * where all jumps point to the JSOP_INTERRUPT case. The latter, after
+ * where all jumps point to the interrupt label. The latter, after
* calling the interrupt handler, dispatches through normalJumpTable to
* continue the normal bytecode processing.
*/
-#else
+ interrupt:
+#else /* !JS_THREADED_INTERP */
for (;;) {
advance_pc_by_one:
JS_ASSERT(js_CodeSpec[op].length == 1);
len = 1;
advance_pc:
regs.pc += len;
op = (JSOp) *regs.pc;
-#ifdef DEBUG
+# ifdef DEBUG
if (cx->tracefp)
js_TraceOpcode(cx, len);
-#endif
+# endif
do_op:
- switchOp = op & switchMask;
+ CHECK_RECORDER();
+ switchOp = intN(op) | switchMask;
do_switch:
switch (switchOp) {
+ case -1:
+ JS_ASSERT(switchMask == -1);
#endif /* !JS_THREADED_INTERP */
-
- BEGIN_CASE(JSOP_INTERRUPT)
{
- JSTrapHandler handler;
-
- handler = cx->debugHooks->interruptHandler;
+ bool moreInterrupts = false;
+ JSTrapHandler handler = cx->debugHooks->interruptHandler;
if (handler) {
+#ifdef JS_TRACER
+ if (TRACE_RECORDER(cx))
+ js_AbortRecording(cx, "interrupt handler");
+#endif
switch (handler(cx, script, regs.pc, &rval,
cx->debugHooks->interruptHandlerData)) {
case JSTRAP_ERROR:
goto error;
case JSTRAP_CONTINUE:
break;
case JSTRAP_RETURN:
fp->rval = rval;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
}
-#if !JS_THREADED_INTERP
- } else {
- /* this was not a real interrupt, the tracer is trying to
- record a trace */
- switchOp = op + 256;
- goto do_switch;
-#endif
+ moreInterrupts = true;
}
- LOAD_INTERRUPT_HANDLER(cx);
+
+#ifdef JS_TRACER
+ TraceRecorder* tr = TRACE_RECORDER(cx);
+ if (tr) {
+ JSMonitorRecordingStatus status = tr->monitorRecording(op);
+ if (status == JSMRS_CONTINUE) {
+ moreInterrupts = true;
+ } else if (status == JSMRS_IMACRO) {
+ atoms = COMMON_ATOMS_START(&rt->atomState);
+ op = JSOp(*regs.pc);
+ DO_OP(); /* keep interrupting for op. */
+ } else {
+ JS_ASSERT(status == JSMRS_STOP);
+ }
+ }
+#endif /* !JS_TRACER */
#if JS_THREADED_INTERP
+ jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
JS_EXTENSION_(goto *normalJumpTable[op]);
#else
- switchOp = op;
+ switchMask = moreInterrupts ? -1 : 0;
+ switchOp = intN(op);
goto do_switch;
#endif
}
/* No-ops for ease of decompilation. */
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
@@ -3019,17 +2996,17 @@ js_Interpret(JSContext *cx)
if (hook) {
/*
* Do not pass &ok directly as exposing the address
* inhibits optimizations and uninitialised warnings.
*/
status = ok;
hook(cx, fp, JS_FALSE, &status, hookData);
ok = status;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
}
}
/*
* If fp has a call object, sync values and clear the back-
* pointer. This can happen for a lightweight function if it
* calls eval unexpectedly (in a way that is hidden from the
* compiler). See bug 325540.
@@ -3234,27 +3211,27 @@ js_Interpret(JSContext *cx)
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
END_CASE(JSOP_IN)
BEGIN_CASE(JSOP_ITER)
JS_ASSERT(regs.sp > StackBase(fp));
flags = regs.pc[1];
if (!js_ValueToIterator(cx, flags, ®s.sp[-1]))
goto error;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
PUSH(JSVAL_VOID);
END_CASE(JSOP_ITER)
BEGIN_CASE(JSOP_NEXTITER)
JS_ASSERT(regs.sp - 2 >= StackBase(fp));
JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1]))
goto error;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
PUSH(rval);
TRACE_0(IteratorNextComplete);
END_CASE(JSOP_NEXTITER)
BEGIN_CASE(JSOP_ENDITER)
/*
* Decrease the stack pointer even when !ok -- see comments in the
@@ -4650,20 +4627,18 @@ js_Interpret(JSContext *cx)
if (entry)
TRACE_1(SetPropMiss, entry);
#endif
} else {
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
goto error;
}
#ifdef JS_TRACER
- if (!entry && TRACE_RECORDER(cx)) {
+ if (!entry && TRACE_RECORDER(cx))
js_AbortRecording(cx, "SetPropUncached");
- ENABLE_TRACER(0);
- }
#endif
} while (0);
END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
BEGIN_CASE(JSOP_GETELEM)
/* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
lval = FETCH_OPND(-2);
rval = FETCH_OPND(-1);
@@ -4803,17 +4778,17 @@ js_Interpret(JSContext *cx)
flags = JSFRAME_CONSTRUCTING;
goto inline_call;
}
}
if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
goto error;
regs.sp = vp + 1;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
END_CASE(JSOP_NEW)
BEGIN_CASE(JSOP_CALL)
BEGIN_CASE(JSOP_EVAL)
BEGIN_CASE(JSOP_APPLY)
argc = GET_ARGC(regs.pc);
vp = regs.sp - (argc + 2);
@@ -4943,17 +4918,17 @@ js_Interpret(JSContext *cx)
while (nvars--)
*newsp++ = JSVAL_VOID;
/* Call the debugger hook if present. */
hook = cx->debugHooks->callHook;
if (hook) {
newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
cx->debugHooks->callHookData);
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
} else {
newifp->hookData = NULL;
}
/* Scope with a call object parented by callee's parent. */
if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
!js_GetCallObject(cx, &newifp->frame, parent)) {
goto bad_inline_call;
@@ -5041,17 +5016,17 @@ js_Interpret(JSContext *cx)
if (VALUE_IS_FUNCTION(cx, lval)) {
if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
jsdtrace_function_rval(cx, fp, fun);
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
jsdtrace_function_return(cx, fp, fun);
}
#endif
regs.sp = vp + 1;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
if (!ok)
goto error;
JS_RUNTIME_METER(rt, nonInlineCalls);
end_call:
#if JS_HAS_LVALUE_RETURN
if (cx->rval2set) {
/*
@@ -5079,17 +5054,17 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_CALL)
#if JS_HAS_LVALUE_RETURN
BEGIN_CASE(JSOP_SETCALL)
argc = GET_ARGC(regs.pc);
vp = regs.sp - argc - 2;
ok = js_Invoke(cx, argc, vp, 0);
regs.sp = vp + 1;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
if (!ok)
goto error;
if (!cx->rval2set) {
op2 = (JSOp) regs.pc[JSOP_SETCALL_LENGTH];
if (op2 != JSOP_DELELEM) {
JS_ASSERT(!(js_CodeSpec[op2].format & JOF_DEL));
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_LEFTSIDE_OF_ASS);
@@ -5517,17 +5492,17 @@ js_Interpret(JSContext *cx)
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
break;
}
JS_ASSERT(status == JSTRAP_CONTINUE);
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
JS_ASSERT(JSVAL_IS_INT(rval));
op = (JSOp) JSVAL_TO_INT(rval);
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
DO_OP();
}
BEGIN_CASE(JSOP_ARGUMENTS)
if (!js_GetArgsValue(cx, fp, &rval))
@@ -5849,17 +5824,17 @@ js_Interpret(JSContext *cx)
obj = FUN_OBJECT(fun);
if (OBJ_GET_PARENT(cx, obj) != parent) {
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj)
goto error;
}
TRACE_2(DefLocalFunSetSlot, slot, obj);
-
+
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
END_CASE(JSOP_DEFLOCALFUN)
BEGIN_CASE(JSOP_ANONFUNOBJ)
/* Load the specified function object literal. */
LOAD_FUNCTION(0);
/* If re-parenting, push a clone of the function object. */
@@ -6074,17 +6049,17 @@ js_Interpret(JSContext *cx)
JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
obj = (i == JSProto_Array)
? js_NewArrayObject(cx, 0, NULL)
: js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
if (!obj)
goto error;
PUSH_OPND(OBJECT_TO_JSVAL(obj));
fp->sharpDepth++;
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
END_CASE(JSOP_NEWINIT)
BEGIN_CASE(JSOP_ENDINIT)
if (--fp->sharpDepth == 0)
fp->sharpArray = NULL;
/* Re-set the newborn root to the top of this object tree. */
JS_ASSERT(regs.sp - StackBase(fp) >= 1);
@@ -6440,17 +6415,17 @@ js_Interpret(JSContext *cx)
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
goto error;
default:;
}
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
}
}
END_CASE(JSOP_DEBUGGER)
#endif /* JS_HAS_DEBUGGER_KEYWORD */
#if JS_HAS_XML_SUPPORT
BEGIN_CASE(JSOP_DEFXMLNS)
rval = POP();
@@ -6854,16 +6829,17 @@ js_Interpret(JSContext *cx)
L_JSOP_TOATTRNAME:
L_JSOP_QNAME:
L_JSOP_QNAMECONST:
L_JSOP_QNAMEPART:
L_JSOP_ANYNAME:
L_JSOP_DEFXMLNS:
# endif
+ L_JSOP_UNUSED135:
L_JSOP_UNUSED203:
L_JSOP_UNUSED204:
L_JSOP_UNUSED205:
L_JSOP_UNUSED206:
L_JSOP_UNUSED207:
L_JSOP_UNUSED208:
L_JSOP_UNUSED209:
L_JSOP_UNUSED219:
@@ -6875,34 +6851,19 @@ js_Interpret(JSContext *cx)
{
char numBuf[12];
JS_snprintf(numBuf, sizeof numBuf, "%d", op);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_BYTECODE, numBuf);
goto error;
}
-#ifdef JS_TRACER
-
-#if JS_THREADED_INTERP
-# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format) \
- R_##x: RECORD(x); goto L_##x;
-#else
-# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format) \
- case 256 + x: RECORD(x); op = x; switchOp = x; goto do_switch;
-#endif
-#include "jsopcode.tbl"
-#undef OPDEF
-
-#endif /* JS_TRACER */
-
#if !JS_THREADED_INTERP
-
} /* switch (op) */
- }
+ } /* for (;;) */
#endif /* !JS_THREADED_INTERP */
error:
if (fp->imacpc && cx->throwing) {
// To keep things simple, we hard-code imacro exception handlers here.
if (*fp->imacpc == JSOP_NEXTITER) {
JS_ASSERT(*regs.pc == JSOP_CALL);
if (js_ValueIsStopIteration(cx->exception)) {
@@ -6942,17 +6903,17 @@ js_Interpret(JSContext *cx)
fp->rval = rval;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->exception = rval;
case JSTRAP_CONTINUE:
default:;
}
- LOAD_INTERRUPT_HANDLER(cx);
+ CHECK_INTERRUPT_HANDLER();
}
/*
* Look for a try block in script that can catch this exception.
*/
if (script->trynotesOffset == 0)
goto no_catch;
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -100,18 +100,22 @@
* argument lists, so they have the lowest precedence.
*
* This file is best viewed with 128 columns:
12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
*/
/* legend: op val name image len use def prec format */
+/*
+ * Generic nop for the decompiler.
+ */
+OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
+
/* Longstanding JavaScript bytecodes. */
-OPDEF(JSOP_INTERRUPT, 0, "interrupt", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP)
OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING)
OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_PARENHEAD)
@@ -344,20 +348,17 @@ OPDEF(JSOP_SETCALL, 132, "setcall",
/*
* Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
* srcnote-annotated JSOP_NOPs and to simply stack balance handling.
*/
OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
-/*
- * Generic nop for the decompiler.
- */
-OPDEF(JSOP_NOP, 135,"nop", NULL, 1, 0, 0, 0, JOF_BYTE)
+OPDEF(JSOP_UNUSED135, 135,"unused135", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Bytecodes that avoid making an arguments object in most cases:
* JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1].
* JSOP_ARGCNT returns fp->argc.
*/
OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME)
OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3829,70 +3829,107 @@ monitor_loop:
return js_AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL);
return false;
default:
/* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
return false;
}
}
-JS_REQUIRES_STACK bool
-js_MonitorRecording(TraceRecorder* tr)
-{
- JSContext* cx = tr->cx;
-
- if (tr->lirbuf->outOMem()) {
+JS_REQUIRES_STACK JSMonitorRecordingStatus
+TraceRecorder::monitorRecording(JSOp op)
+{
+ if (lirbuf->outOMem()) {
js_AbortRecording(cx, "no more LIR memory");
js_FlushJITCache(cx);
- return false;
- }
-
- // Process deepAbort() requests now.
- if (tr->wasDeepAborted()) {
+ return JSMRS_STOP;
+ }
+
+ /* Process deepAbort() requests now. */
+ if (wasDeepAborted()) {
js_AbortRecording(cx, "deep abort requested");
- return false;
- }
-
- if (tr->walkedOutOfLoop())
- return js_CloseLoop(cx);
-
- // Clear one-shot state used to communicate between record_JSOP_CALL and post-
- // opcode-case-guts record hook (record_FastNativeCallComplete).
- tr->pendingTraceableNative = NULL;
-
- // In the future, handle dslots realloc by computing an offset from dslots instead.
- if (tr->global_dslots != tr->globalObj->dslots) {
- js_AbortRecording(cx, "globalObj->dslots reallocated");
- return false;
- }
-
- jsbytecode* pc = cx->fp->regs->pc;
-
- /* If we hit a break, end the loop and generate an always taken loop exit guard. For other
- downward gotos (like if/else) continue recording. */
- if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
- jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
- if (sn && SN_TYPE(sn) == SRC_BREAK) {
- AUDIT(breakLoopExits);
- tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
+ return JSMRS_STOP;
+ }
+
+ if (walkedOutOfLoop()) {
+ if (!js_CloseLoop(cx))
+ return JSMRS_STOP;
+ } else {
+ // Clear one-shot state used to communicate between record_JSOP_CALL and post-
+ // opcode-case-guts record hook (record_FastNativeCallComplete).
+ pendingTraceableNative = NULL;
+
+ // In the future, handle dslots realloc by computing an offset from dslots instead.
+ if (global_dslots != globalObj->dslots) {
+ js_AbortRecording(cx, "globalObj->dslots reallocated");
+ return JSMRS_STOP;
+ }
+
+ jsbytecode* pc = cx->fp->regs->pc;
+
+ /* If we hit a break, end the loop and generate an always taken loop exit guard. For other
+ downward gotos (like if/else) continue recording. */
+ if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
+ jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
+ if (sn && SN_TYPE(sn) == SRC_BREAK) {
+ AUDIT(breakLoopExits);
+ endLoop(JS_TRACE_MONITOR(cx).fragmento);
+ js_DeleteRecorder(cx);
+ return JSMRS_STOP; /* done recording */
+ }
+ }
+
+ /* An explicit return from callDepth 0 should end the loop, not abort it. */
+ if (*pc == JSOP_RETURN && callDepth == 0) {
+ AUDIT(returnLoopExits);
+ endLoop(JS_TRACE_MONITOR(cx).fragmento);
js_DeleteRecorder(cx);
- return false; /* done recording */
+ return JSMRS_STOP; /* done recording */
}
}
- /* An explicit return from callDepth 0 should end the loop, not abort it. */
- if (*pc == JSOP_RETURN && tr->callDepth == 0) {
- AUDIT(returnLoopExits);
- tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
- js_DeleteRecorder(cx);
- return false; /* done recording */
- }
-
/* If it's not a break or a return from a loop, continue recording and follow the trace. */
- return true;
+
+ /* We check for imacro-calling bytecodes inside the switch cases to resolve
+ the "if" condition at the compile time. */
+ bool flag;
+ switch (op) {
+ default: goto abort_recording;
+# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format) \
+ case x: \
+ flag = record_##x(); \
+ if (x == JSOP_ITER || x == JSOP_NEXTITER || x == JSOP_APPLY || \
+ JSOP_IS_BINARY(x) || JSOP_IS_UNARY(op)) { \
+ goto imacro; \
+ } \
+ break;
+# include "jsopcode.tbl"
+# undef OPDEF
+ }
+
+ if (flag)
+ return JSMRS_CONTINUE;
+ goto abort_recording;
+
+ imacro:
+ /* We save macro-generated code size also via bool TraceRecorder::record_JSOP_*
+ return type, instead of a three-state: OK, ABORTED, IMACRO_STARTED. But the
+ price of this is the JSFRAME_IMACRO_START frame flag. We need one more bit
+ to detect that TraceRecorder::call_imacro was invoked by the record_JSOP_
+ method. */
+ if (flag)
+ return JSMRS_CONTINUE;
+ if (cx->fp->flags & JSFRAME_IMACRO_START) {
+ cx->fp->flags &= ~JSFRAME_IMACRO_START;
+ return JSMRS_IMACRO;
+ }
+
+ abort_recording:
+ js_AbortRecording(cx, js_CodeName[op]);
+ return JSMRS_STOP;
}
/* If used on a loop trace, blacklists the root peer instead of the given fragment. */
void
js_BlacklistPC(Fragmento* frago, Fragment* frag)
{
if (frag->kind == LoopTrace)
frag = frago->getLoop(frag->ip);
@@ -5499,22 +5536,16 @@ TraceRecorder::record_LeaveFrame()
// LeaveFrame gets called after the interpreter popped the frame and
// stored rval, so cx->fp not cx->fp->down, and -1 not 0.
atoms = cx->fp->script->atomMap.vector;
set(&stackval(-1), rval_ins, true);
return true;
}
JS_REQUIRES_STACK bool
-TraceRecorder::record_JSOP_INTERRUPT()
-{
- return false;
-}
-
-JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_PUSH()
{
stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
return true;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_POPV()
@@ -6830,17 +6861,17 @@ TraceRecorder::record_FastNativeCallComp
default:
/* Convert the result to double if the builtin returns int32. */
if (JSVAL_IS_NUMBER(v) &&
(pendingTraceableNative->builtin->_argtypes & 3) == nanojit::ARGSIZE_LO) {
set(&v, lir->ins1(LIR_i2f, v_ins));
}
}
- // We'll null pendingTraceableNative in js_MonitorRecording, on the next op cycle.
+ // We'll null pendingTraceableNative in monitorRecording, on the next op cycle.
// There must be a next op since the stack is non-empty.
return ok;
}
JS_REQUIRES_STACK bool
TraceRecorder::name(jsval*& vp)
{
JSObject* obj = cx->fp->scopeChain;
@@ -8619,16 +8650,17 @@ InitIMacroCode()
#define UNUSED(n) \
JS_REQUIRES_STACK bool \
TraceRecorder::record_JSOP_UNUSED##n() { \
JS_NOT_REACHED("JSOP_UNUSED" # n); \
return false; \
}
+UNUSED(135)
UNUSED(203)
UNUSED(204)
UNUSED(205)
UNUSED(206)
UNUSED(207)
UNUSED(208)
UNUSED(209)
UNUSED(219)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -256,16 +256,22 @@ struct FrameInfo {
struct {
uint16 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL
uint16 argc; // actual argument count, may be < fun->nargs
} s;
uint32 word; // for spdist/argc LIR store in record_JSOP_CALL
};
};
+enum JSMonitorRecordingStatus {
+ JSMRS_CONTINUE,
+ JSMRS_STOP,
+ JSMRS_IMACRO
+};
+
class TraceRecorder : public avmplus::GCObject {
JSContext* cx;
JSTraceMonitor* traceMonitor;
JSObject* globalObj;
Tracker tracker;
Tracker nativeFrameTracker;
char* entryTypeMap;
unsigned callDepth;
@@ -311,16 +317,17 @@ class TraceRecorder : public avmplus::GC
unsigned callDepth, uint8* globalTypeMap, uint8* stackTypeMap);
void trackNativeStackUse(unsigned slots);
JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
JS_REQUIRES_STACK nanojit::LIns* guard(bool expected, nanojit::LIns* cond,
ExitType exitType);
nanojit::LIns* guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
+
nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
JS_REQUIRES_STACK nanojit::LIns* get(jsval* p) const;
nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset);
JS_REQUIRES_STACK void set(jsval* p, nanojit::LIns* l, bool initializing = false);
JS_REQUIRES_STACK bool checkType(jsval& v, uint8 t, jsval*& stage_val,
nanojit::LIns*& stage_ins, unsigned& stage_count);
@@ -419,24 +426,24 @@ class TraceRecorder : public avmplus::GC
JS_REQUIRES_STACK void trackCfgMerges(jsbytecode* pc);
JS_REQUIRES_STACK void flipIf(jsbytecode* pc, bool& cond);
JS_REQUIRES_STACK void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
bool hasMethod(JSObject* obj, jsid id);
bool hasIteratorMethod(JSObject* obj);
public:
- friend JS_REQUIRES_STACK bool js_MonitorRecording(TraceRecorder* tr);
-
JS_REQUIRES_STACK
TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
VMSideExit* expectedInnerExit, nanojit::Fragment* outerToBlacklist);
~TraceRecorder();
+ JS_REQUIRES_STACK JSMonitorRecordingStatus monitorRecording(JSOp op);
+
JS_REQUIRES_STACK uint8 determineSlotType(jsval* vp) const;
JS_REQUIRES_STACK nanojit::LIns* snapshot(ExitType exitType);
nanojit::Fragment* getFragment() const { return fragment; }
JS_REQUIRES_STACK bool isLoopHeader(JSContext* cx) const;
JS_REQUIRES_STACK void compile(nanojit::Fragmento* fragmento);
JS_REQUIRES_STACK bool closeLoop(nanojit::Fragmento* fragmento, bool& demote,
unsigned *demotes);
JS_REQUIRES_STACK void endLoop(nanojit::Fragmento* fragmento);
@@ -476,68 +483,36 @@ public:
};
#define TRACING_ENABLED(cx) JS_HAS_OPTION(cx, JSOPTION_JIT)
#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder)
#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
#define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
#define JSOP_IS_UNARY(op) ((uintN)((op) - JSOP_NEG) <= (uintN)(JSOP_POS - JSOP_NEG))
-/*
- * See jsinterp.cpp for the ENABLE_TRACER definition. Also note how comparing x
- * to JSOP_* constants specializes trace-recording code at compile time either
- * to include imacro support, or exclude it altogether for this particular x.
- *
- * We save macro-generated code size also via bool TraceRecorder::record_JSOP_*
- * return type, instead of a three-state: OK, ABORTED, IMACRO_STARTED. But the
- * price of this is the JSFRAME_IMACRO_START frame flag. We need one more bit
- * to detect that TraceRecorder::call_imacro was invoked by the record_JSOP_*
- * method invoked by TRACE_ARGS_.
- */
-#define RECORD_ARGS(x,args) \
+#define TRACE_ARGS_(x,args) \
JS_BEGIN_MACRO \
- if (!js_MonitorRecording(TRACE_RECORDER(cx))) { \
- ENABLE_TRACER(0); \
- } else { \
- TRACE_ARGS_(x, args, \
- if ((fp->flags & JSFRAME_IMACRO_START) && \
- (x == JSOP_ITER || x == JSOP_NEXTITER || \
- x == JSOP_APPLY || JSOP_IS_BINARY(x) || \
- JSOP_IS_UNARY(op))) { \
- fp->flags &= ~JSFRAME_IMACRO_START; \
- atoms = COMMON_ATOMS_START(&rt->atomState); \
- op = JSOp(*regs.pc); \
- DO_OP(); \
- } \
- ); \
- } \
+ TraceRecorder* tr_ = TRACE_RECORDER(cx); \
+ if (tr_ && !tr_->record_##x args) \
+ js_AbortRecording(cx, #x); \
JS_END_MACRO
-#define TRACE_ARGS_(x,args,onfalse) \
- JS_BEGIN_MACRO \
- TraceRecorder* tr_ = TRACE_RECORDER(cx); \
- if (tr_ && !tr_->record_##x args) { \
- onfalse \
- js_AbortRecording(cx, #x); \
- ENABLE_TRACER(0); \
- } \
- JS_END_MACRO
-
-#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args, )
-
-#define RECORD(x) RECORD_ARGS(x, ())
+#define TRACE_ARGS(x,args) TRACE_ARGS_(x, args)
#define TRACE_0(x) TRACE_ARGS(x, ())
#define TRACE_1(x,a) TRACE_ARGS(x, (a))
#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b))
extern JS_REQUIRES_STACK bool
js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount);
-extern JS_REQUIRES_STACK bool
-js_MonitorRecording(TraceRecorder *tr);
+#ifdef DEBUG
+# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx, reason)
+#else
+# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx)
+#endif
extern JS_REQUIRES_STACK void
js_AbortRecording(JSContext* cx, const char* reason);
extern void
js_InitJIT(JSTraceMonitor *tm);
extern void
@@ -546,16 +521,15 @@ js_FinishJIT(JSTraceMonitor *tm);
extern void
js_FlushJITCache(JSContext* cx);
extern void
js_FlushJITOracle(JSContext* cx);
#else /* !JS_TRACER */
-#define RECORD(x) ((void)0)
#define TRACE_0(x) ((void)0)
#define TRACE_1(x,a) ((void)0)
#define TRACE_2(x,a,b) ((void)0)
#endif /* !JS_TRACER */
#endif /* jstracer_h___ */
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
* Bytecode version number. Increment the subtrahend whenever JS bytecode
* changes incompatibly.
*
* This version number should be XDR'ed once near the front of any file or
* larger storage unit containing XDR'ed bytecode and other data, and checked
* 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 - 38)
+#define JSXDR_BYTECODE_VERSION (0xb973c0de - 39)
/*
* Library-private functions.
*/
extern JSBool
js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
extern JSBool