bug 469233 - using interrupt hook support in the interpreter for trace recording. r=brendan

This commit is contained in:
Igor Bukanov 2008-12-21 12:55:09 +01:00
Родитель 6c27090c69
Коммит 7c434bc745
5 изменённых файлов: 183 добавлений и 215 удалений

Просмотреть файл

@ -242,8 +242,8 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
/* 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. */
@ -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, &regs.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]), &regs.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);
@ -5854,7 +5829,7 @@ js_Interpret(JSContext *cx)
}
TRACE_2(DefLocalFunSetSlot, slot, obj);
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
END_CASE(JSOP_DEFLOCALFUN)
@ -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.