diff --git a/js/src/jit-test/tests/basic/testWhileWithContinue.js b/js/src/jit-test/tests/basic/testWhileWithContinue.js new file mode 100644 index 000000000000..6a8e5b5919e9 --- /dev/null +++ b/js/src/jit-test/tests/basic/testWhileWithContinue.js @@ -0,0 +1,10 @@ +// Test using 'while' with 'continue' -- the most ancient, the most powerful, +// the most rare of all coding incantations. + +var i = 0; +while (i < HOTLOOP+4) { + ++i; + continue; +} +assertEq(i, HOTLOOP+4); + diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ab6961dc46a0..f6fc1553b6cb 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6885,6 +6885,38 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr) state.innermost = innermost; } +static jsbytecode * +GetLoopBottom(JSContext *cx, jsbytecode *pc) +{ + JS_ASSERT(*pc == JSOP_TRACE || *pc == JSOP_NOTRACE); + JSScript *script = cx->fp()->script(); + jssrcnote *sn = js_GetSrcNote(script, pc); + if (!sn) + return NULL; + return pc + js_GetSrcNoteOffset(sn, 0); +} + +JS_ALWAYS_INLINE void +TraceRecorder::assertInsideLoop() +{ +#ifdef DEBUG + /* Asserts at callDepth == 0 will catch problems at the call op. */ + if (callDepth > 0) + return; + + jsbytecode *pc = cx->regs->fp->hasImacropc() ? cx->regs->fp->imacropc() : cx->regs->pc; + jsbytecode *beg = (jsbytecode *)tree->ip; + jsbytecode *end = GetLoopBottom(cx, beg); + + /* + * In some cases (continue in a while loop), we jump to the goto + * immediately preceeding a loop (the one that jumps to the loop + * condition). + */ + JS_ASSERT(pc >= beg - JSOP_GOTO_LENGTH && pc <= end); +#endif +} + JS_REQUIRES_STACK MonitorResult RecordLoopEdge(JSContext* cx, uintN& inlineCallCount) { @@ -6896,6 +6928,7 @@ RecordLoopEdge(JSContext* cx, uintN& inlineCallCount) /* Is the recorder currently active? */ if (tm->recorder) { + tm->recorder->assertInsideLoop(); jsbytecode* pc = cx->regs->pc; if (pc == tm->recorder->tree->ip) { tm->recorder->closeLoop(); @@ -6905,7 +6938,6 @@ RecordLoopEdge(JSContext* cx, uintN& inlineCallCount) if (r == MONITOR_RECORDING || r == MONITOR_ERROR) return r; - /* * recordLoopEdge will invoke an inner tree if we have a matching * one. If we arrive here, that tree didn't run to completion and @@ -7102,6 +7134,7 @@ TraceRecorder::monitorRecording(JSOp op) { TraceMonitor &localtm = JS_TRACE_MONITOR(cx); debug_only_stmt( JSContext *localcx = cx; ) + assertInsideLoop(); /* Process needFlush requests now. */ if (localtm.needFlush) { @@ -10286,7 +10319,9 @@ TraceRecorder::record_JSOP_GOTO() */ jssrcnote* sn = js_GetSrcNote(cx->fp()->script(), cx->regs->pc); - if (sn && (SN_TYPE(sn) == SRC_BREAK || SN_TYPE(sn) == SRC_CONT2LABEL)) { + if (sn && + (SN_TYPE(sn) == SRC_BREAK || SN_TYPE(sn) == SRC_CONT2LABEL || + SN_TYPE(sn) == SRC_BREAK2LABEL)) { AUDIT(breakLoopExits); return endLoop(); } @@ -16308,12 +16343,7 @@ static const uintN MAX_PROFILE_OPS = 4096; static jsbytecode * GetLoopBottom(JSContext *cx) { - jsbytecode* pc = cx->regs->pc; - JSScript *script = cx->fp()->script(); - jssrcnote *sn = js_GetSrcNote(script, pc); - if (!sn) - return NULL; - return pc + js_GetSrcNoteOffset(sn, 0); + return GetLoopBottom(cx, cx->regs->pc); } static LoopProfile * diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 2be11bd8ad71..ab3d6cee2aba 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1201,6 +1201,7 @@ class TraceRecorder #ifdef DEBUG bool isValidFrameObjPtr(void *obj); #endif + void assertInsideLoop(); JS_REQUIRES_STACK void setImpl(void* p, nanojit::LIns* l, bool demote = true); JS_REQUIRES_STACK void set(Value* p, nanojit::LIns* l, bool demote = true);