зеркало из https://github.com/mozilla/gecko-dev.git
bug 469233 - using interrupt hook support in the interpreter for trace recording. r=brendan
This commit is contained in:
Родитель
6c27090c69
Коммит
7c434bc745
|
@ -2415,12 +2415,6 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
|
|||
# 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.
|
||||
|
@ -2477,12 +2471,6 @@ js_Interpret(JSContext *cx)
|
|||
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
|
||||
|
@ -2506,29 +2494,22 @@ js_Interpret(JSContext *cx)
|
|||
# undef OPDEF
|
||||
};
|
||||
|
||||
#ifdef JS_TRACER
|
||||
static void *const recordingJumpTable[] = {
|
||||
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
|
||||
JS_EXTENSION &&R_##op,
|
||||
# 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,
|
||||
JS_EXTENSION &&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
|
||||
|
@ -2543,8 +2524,7 @@ js_Interpret(JSContext *cx)
|
|||
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) \
|
||||
|
@ -2556,13 +2536,25 @@ js_Interpret(JSContext *cx)
|
|||
|
||||
#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
|
||||
|
@ -2649,7 +2641,10 @@ js_Interpret(JSContext *cx)
|
|||
#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 \
|
||||
|
@ -2718,53 +2713,19 @@ js_Interpret(JSContext *cx)
|
|||
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);
|
||||
|
@ -2820,11 +2781,12 @@ js_Interpret(JSContext *cx)
|
|||
* 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);
|
||||
|
@ -2832,23 +2794,27 @@ js_Interpret(JSContext *cx)
|
|||
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:
|
||||
|
@ -2865,20 +2831,31 @@ js_Interpret(JSContext *cx)
|
|||
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
|
||||
}
|
||||
|
@ -3024,7 +3001,7 @@ js_Interpret(JSContext *cx)
|
|||
status = ok;
|
||||
hook(cx, fp, JS_FALSE, &status, hookData);
|
||||
ok = status;
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3239,7 +3216,7 @@ js_Interpret(JSContext *cx)
|
|||
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)
|
||||
|
@ -3249,7 +3226,7 @@ js_Interpret(JSContext *cx)
|
|||
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);
|
||||
|
@ -4655,10 +4632,8 @@ js_Interpret(JSContext *cx)
|
|||
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);
|
||||
|
@ -4808,7 +4783,7 @@ js_Interpret(JSContext *cx)
|
|||
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)
|
||||
|
@ -4948,7 +4923,7 @@ js_Interpret(JSContext *cx)
|
|||
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;
|
||||
}
|
||||
|
@ -5046,7 +5021,7 @@ js_Interpret(JSContext *cx)
|
|||
}
|
||||
#endif
|
||||
regs.sp = vp + 1;
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
if (!ok)
|
||||
goto error;
|
||||
JS_RUNTIME_METER(rt, nonInlineCalls);
|
||||
|
@ -5084,7 +5059,7 @@ js_Interpret(JSContext *cx)
|
|||
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) {
|
||||
|
@ -5522,7 +5497,7 @@ js_Interpret(JSContext *cx)
|
|||
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);
|
||||
|
@ -6079,7 +6054,7 @@ js_Interpret(JSContext *cx)
|
|||
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)
|
||||
|
@ -6445,7 +6420,7 @@ js_Interpret(JSContext *cx)
|
|||
goto error;
|
||||
default:;
|
||||
}
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_DEBUGGER)
|
||||
|
@ -6859,6 +6834,7 @@ js_Interpret(JSContext *cx)
|
|||
L_JSOP_DEFXMLNS:
|
||||
# endif
|
||||
|
||||
L_JSOP_UNUSED135:
|
||||
L_JSOP_UNUSED203:
|
||||
L_JSOP_UNUSED204:
|
||||
L_JSOP_UNUSED205:
|
||||
|
@ -6880,24 +6856,9 @@ js_Interpret(JSContext *cx)
|
|||
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:
|
||||
|
@ -6947,7 +6908,7 @@ js_Interpret(JSContext *cx)
|
|||
case JSTRAP_CONTINUE:
|
||||
default:;
|
||||
}
|
||||
LOAD_INTERRUPT_HANDLER(cx);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -105,8 +105,12 @@
|
|||
|
||||
/* 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)
|
||||
|
@ -349,10 +353,7 @@ OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16
|
|||
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:
|
||||
|
|
|
@ -3834,60 +3834,97 @@ monitor_loop:
|
|||
}
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
js_MonitorRecording(TraceRecorder* tr)
|
||||
JS_REQUIRES_STACK JSMonitorRecordingStatus
|
||||
TraceRecorder::monitorRecording(JSOp op)
|
||||
{
|
||||
JSContext* cx = tr->cx;
|
||||
|
||||
if (tr->lirbuf->outOMem()) {
|
||||
if (lirbuf->outOMem()) {
|
||||
js_AbortRecording(cx, "no more LIR memory");
|
||||
js_FlushJITCache(cx);
|
||||
return false;
|
||||
return JSMRS_STOP;
|
||||
}
|
||||
|
||||
// Process deepAbort() requests now.
|
||||
if (tr->wasDeepAborted()) {
|
||||
/* Process deepAbort() requests now. */
|
||||
if (wasDeepAborted()) {
|
||||
js_AbortRecording(cx, "deep abort requested");
|
||||
return false;
|
||||
return JSMRS_STOP;
|
||||
}
|
||||
|
||||
if (tr->walkedOutOfLoop())
|
||||
return js_CloseLoop(cx);
|
||||
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;
|
||||
|
||||
// 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 (global_dslots != globalObj->dslots) {
|
||||
js_AbortRecording(cx, "globalObj->dslots reallocated");
|
||||
return JSMRS_STOP;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
/* 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. */
|
||||
|
||||
/* 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(x)) { \
|
||||
goto imacro; \
|
||||
} \
|
||||
break;
|
||||
# include "jsopcode.tbl"
|
||||
# undef OPDEF
|
||||
}
|
||||
|
||||
/* If it's not a break or a return from a loop, continue recording and follow the trace. */
|
||||
return true;
|
||||
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. */
|
||||
|
@ -5503,12 +5540,6 @@ TraceRecorder::record_LeaveFrame()
|
|||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_INTERRUPT()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_PUSH()
|
||||
{
|
||||
|
@ -6835,7 +6866,7 @@ TraceRecorder::record_FastNativeCallComplete()
|
|||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -8624,6 +8655,7 @@ InitIMacroCode()
|
|||
return false; \
|
||||
}
|
||||
|
||||
UNUSED(135)
|
||||
UNUSED(203)
|
||||
UNUSED(204)
|
||||
UNUSED(205)
|
||||
|
|
|
@ -261,6 +261,12 @@ struct FrameInfo {
|
|||
};
|
||||
};
|
||||
|
||||
enum JSMonitorRecordingStatus {
|
||||
JSMRS_CONTINUE,
|
||||
JSMRS_STOP,
|
||||
JSMRS_IMACRO
|
||||
};
|
||||
|
||||
class TraceRecorder : public avmplus::GCObject {
|
||||
JSContext* cx;
|
||||
JSTraceMonitor* traceMonitor;
|
||||
|
@ -316,6 +322,7 @@ class TraceRecorder : public avmplus::GCObject {
|
|||
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;
|
||||
|
@ -424,14 +431,14 @@ class TraceRecorder : public avmplus::GCObject {
|
|||
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; }
|
||||
|
@ -481,49 +488,14 @@ public:
|
|||
#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) \
|
||||
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(); \
|
||||
} \
|
||||
); \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define TRACE_ARGS_(x,args,onfalse) \
|
||||
#define TRACE_ARGS_(x,args) \
|
||||
JS_BEGIN_MACRO \
|
||||
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
|
||||
if (tr_ && !tr_->record_##x args) { \
|
||||
onfalse \
|
||||
if (tr_ && !tr_->record_##x args) \
|
||||
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))
|
||||
|
@ -531,8 +503,11 @@ public:
|
|||
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);
|
||||
|
@ -551,7 +526,6 @@ 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)
|
||||
|
|
|
@ -204,7 +204,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 - 38)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 39)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
Загрузка…
Ссылка в новой задаче