From caf954749b53c7b92040eda0ce8ee937a0bf4f5e Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Sun, 21 Dec 2008 00:16:41 +0100 Subject: [PATCH] bug 469233 - using interrupt hook support in the interpreter for trace recording. r=brendan --- js/src/jsinterp.cpp | 207 +++++++++++++++++++++------------------------------- js/src/jsopcode.tbl | 11 +-- js/src/jstracer.cpp | 120 +++++++++++++++++++----------- js/src/jstracer.h | 60 +++++---------- js/src/jsxdrapi.h | 2 +- 5 files changed, 184 insertions(+), 216 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index b18e83db9b..d10ca3d5ee 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -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. */ @@ -2416,12 +2416,6 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX))); #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); @@ -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(); } /* diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index ffdb1f82ac..214e152690 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -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: diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index fac4068082..529161da87 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -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; + + // 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; + } - // Clear one-shot state used to communicate between record_JSOP_CALL and post- - // opcode-case-guts record hook (record_FastNativeCallComplete). - tr->pendingTraceableNative = NULL; + 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 */ + } + } - // 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; + /* 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 JSMRS_STOP; /* done recording */ + } } - jsbytecode* pc = cx->fp->regs->pc; + /* If it's not a break or a return from a loop, continue recording and follow the trace. */ - /* 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); - js_DeleteRecorder(cx); - return false; /* done recording */ - } + /* 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) || x == JSOP_IS_UNARY(op)) { \ + goto imacro; \ + } \ + break; +# include "jsopcode.tbl" +# undef OPDEF } - /* 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 (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; } - /* If it's not a break or a return from a loop, continue recording and follow the trace. */ - return true; + 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. */ @@ -5504,12 +5541,6 @@ TraceRecorder::record_LeaveFrame() } 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))); @@ -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) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 27828aba17..022c208d39 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -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) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 47081cb1a0..8d370ffeba 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -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. -- 2.11.4.GIT