Bug 538293 - remove inlineCallCount and this STACK_QUOTA silliness (r=dvander)

--HG--
extra : rebase_source : 724910c67423b0720ca5c3732699bca4d64324ef
This commit is contained in:
Luke Wagner 2011-05-27 18:15:39 -07:00
parent a0b946dbbc
commit 9f59f7c3f5
24 changed files with 271 additions and 379 deletions

View File

@ -1,10 +1,11 @@
var Q = 0;
var thrown = false;
try {
(function f(i) { Q = i; if (i == 100000) return; f(i+1); })(1)
(function f(i) { Q = i; if (i == 200000) return; f(i+1); })(1)
} catch (e) {
thrown = true;
}
// Exact behavior of recursion check depends on which JIT we use.
var ok = (Q == 3000 || Q == 3001);
assertEq(ok, true);
assertEq(thrown && Q > 10000, true);

View File

@ -3,7 +3,7 @@ try {
Function("\
(function f() {\
({x:{b}}=x);\
f()\
f.apply(null, new Array(100))\
})()\
")()
} catch (e) {

View File

@ -3,7 +3,7 @@ try {
Function("\
(function f() {\
({x}=x);\
f()\
f.apply(null, new Array(100))\
})()\
")()
} catch (e) {

View File

@ -0,0 +1,11 @@
function f(x) {
if (x == 0)
return;
arguments[0]--;
f.apply(null, arguments);
}
a = [100];
for (var i = 0; i < 2000; ++i)
a.push(i);
f.apply(null, a);

View File

@ -767,10 +767,10 @@ PopulateReportBlame(JSContext *cx, JSErrorReport *report)
* Walk stack until we find a frame that is associated with some script
* rather than a native frame.
*/
for (StackFrame *fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) {
if (fp->pc(cx)) {
report->filename = fp->script()->filename;
report->lineno = js_FramePCToLineNumber(cx, fp);
for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
if (iter.fp()->isScriptFrame()) {
report->filename = iter.fp()->script()->filename;
report->lineno = js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
break;
}
}

View File

@ -1459,13 +1459,6 @@ class AutoCheckRequestDepth {
# define CHECK_REQUEST_THREAD(cx) ((void) 0)
#endif
static inline uintN
FramePCOffset(JSContext *cx, js::StackFrame* fp)
{
jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
return uintN(pc - fp->script()->code);
}
static inline JSAtom **
FrameAtomBase(JSContext *cx, js::StackFrame *fp)
{

View File

@ -102,7 +102,6 @@ struct TracerState
void* rpAtLastTreeCall; // value of rp at innermost tree call guard
VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree
TreeFragment* outermostTree; // the outermost tree we initially invoked
uintN* inlineCallCountp; // inline call count counter
VMSideExit** innermostNestedGuardp;
VMSideExit* innermost;
uint64 startTime;
@ -121,7 +120,7 @@ struct TracerState
js::Value* nativeVp;
TracerState(JSContext *cx, TraceMonitor *tm, TreeFragment *ti,
uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp);
VMSideExit** innermostNestedGuardp);
~TracerState();
};

View File

@ -1429,7 +1429,7 @@ JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
JS_PUBLIC_API(jsbytecode *)
JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
{
return Valueify(fp)->pc(cx);
return Valueify(fp)->pcQuadratic(cx);
}
JS_PUBLIC_API(JSStackFrame *)
@ -2483,9 +2483,9 @@ jstv_Filename(JSStackFrame *fp)
inline uintN
jstv_Lineno(JSContext *cx, JSStackFrame *fp)
{
while (fp && fp->pc(cx) == NULL)
while (fp && fp->pcQuadratic(cx) == NULL)
fp = fp->prev();
return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
return (fp && fp->pcQuadratic(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
}
/* Collect states here and distribute to a matching buffer, if any */

View File

@ -267,7 +267,6 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
JSErrorReporter older;
JSExceptionState *state;
jsid callerid;
StackFrame *fp, *fpstop;
size_t stackDepth, valueCount, size;
JSBool overflow;
JSExnPrivate *priv;
@ -293,7 +292,10 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
stackDepth = 0;
valueCount = 0;
for (fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) {
FrameRegsIter firstPass(cx);
for (; !firstPass.done(); ++firstPass) {
StackFrame *fp = firstPass.fp();
if (fp->compartment() != cx->compartment)
break;
if (fp->isNonEvalFunctionFrame()) {
@ -308,7 +310,6 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
}
JS_RestoreExceptionState(cx, state);
JS_SetErrorReporter(cx, older);
fpstop = fp;
size = offsetof(JSExnPrivate, stackElems);
overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
@ -336,10 +337,11 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
values = GetStackTraceValueBuffer(priv);
elem = priv->stackElems;
for (fp = js_GetTopStackFrame(cx); fp != fpstop; fp = fp->prev()) {
for (FrameRegsIter iter(cx); iter != firstPass; ++iter) {
StackFrame *fp = iter.fp();
if (fp->compartment() != cx->compartment)
break;
if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
if (!fp->isNonEvalFunctionFrame()) {
elem->funName = NULL;
elem->argc = 0;
} else {
@ -354,8 +356,8 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
elem->filename = NULL;
if (fp->isScriptFrame()) {
elem->filename = fp->script()->filename;
if (fp->pc(cx))
elem->ulineno = js_FramePCToLineNumber(cx, fp);
if (fp->isScriptFrame())
elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
}
++elem;
}
@ -692,9 +694,6 @@ FilenameToString(JSContext *cx, const char *filename)
static JSBool
Exception(JSContext *cx, uintN argc, Value *vp)
{
JSString *message, *filename;
StackFrame *fp;
/*
* ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
* called as functions, without operator new. But as we do not give
@ -726,6 +725,7 @@ Exception(JSContext *cx, uintN argc, Value *vp)
/* Set the 'message' property. */
Value *argv = vp + 2;
JSString *message;
if (argc != 0 && !argv[0].isUndefined()) {
message = js_ValueToString(cx, argv[0]);
if (!message)
@ -735,17 +735,21 @@ Exception(JSContext *cx, uintN argc, Value *vp)
message = NULL;
}
/* Find the scripted caller. */
FrameRegsIter iter(cx);
while (!iter.done() && !iter.fp()->isScriptFrame())
++iter;
/* Set the 'fileName' property. */
JSString *filename;
if (argc > 1) {
filename = js_ValueToString(cx, argv[1]);
if (!filename)
return JS_FALSE;
argv[1].setString(filename);
fp = NULL;
} else {
fp = js_GetScriptedCaller(cx, NULL);
if (fp) {
filename = FilenameToString(cx, fp->script()->filename);
if (!iter.done()) {
filename = FilenameToString(cx, iter.fp()->script()->filename);
if (!filename)
return JS_FALSE;
} else {
@ -759,9 +763,7 @@ Exception(JSContext *cx, uintN argc, Value *vp)
if (!ValueToECMAUint32(cx, argv[2], &lineno))
return JS_FALSE;
} else {
if (!fp)
fp = js_GetScriptedCaller(cx, NULL);
lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
}
if (obj->getClass() == &js_ErrorClass &&

View File

@ -149,7 +149,7 @@ js::GetBlockChain(JSContext *cx, StackFrame *fp)
return NULL;
/* Assume that imacros don't affect blockChain */
jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pcQuadratic(cx);
JSScript *script = fp->script();
jsbytecode *start = script->code;
@ -195,7 +195,7 @@ JSObject *
js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
{
/* Assume that we're in a script frame. */
jsbytecode *pc = fp->pc(cx);
jsbytecode *pc = fp->pcQuadratic(cx);
JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
pc += oplen;
@ -2143,7 +2143,7 @@ IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
namespace js {
JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMode interpMode)
Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
{
#ifdef MOZ_TRACEVIS
TraceVisStateObj tvso(cx, S_INTERP);
@ -2312,7 +2312,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo
void *ncode = \
script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);\
interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
if (inlineCallCount) \
if (entryFrame != regs.fp()) \
goto jit_return; \
regs.fp()->setFinishedInInterpreter(); \
goto leave_on_safe_point; \
@ -2365,7 +2365,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMo
if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
MONITOR_BRANCH_METHODJIT(); \
} else { \
MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
MonitorResult r = MonitorLoopEdge(cx, interpMode); \
if (r == MONITOR_RECORDING) { \
JS_ASSERT(TRACE_RECORDER(cx)); \
JS_ASSERT(!TRACE_PROFILER(cx)); \
@ -2856,8 +2856,6 @@ BEGIN_CASE(JSOP_STOP)
/* Resume execution in the calling frame. */
RESET_USE_METHODJIT();
JS_ASSERT(inlineCallCount);
inlineCallCount--;
if (JS_LIKELY(interpReturnOK)) {
JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
== JSOP_CALL_LENGTH);
@ -4618,12 +4616,6 @@ BEGIN_CASE(JSOP_FUNCALL)
goto end_call;
}
/* Restrict recursion of lightweight functions. */
if (JS_UNLIKELY(inlineCallCount >= StackSpace::MAX_INLINE_CALLS)) {
js_ReportOverRecursed(cx);
goto error;
}
/* Get pointer to new frame/slots, prepare arguments. */
ContextStack &stack = cx->stack;
StackFrame *newfp = stack.getInlineFrame(cx, regs.sp, argc, newfun,
@ -4649,7 +4641,6 @@ BEGIN_CASE(JSOP_FUNCALL)
goto error;
RESET_USE_METHODJIT();
inlineCallCount++;
JS_RUNTIME_METER(rt, inlineCalls);
TRACE_0(EnterFrame);

View File

@ -257,7 +257,7 @@ enum InterpMode
* pointed to by cx->fp until completion or error.
*/
extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, StackFrame *stopFp, uintN inlineCallCount = 0, InterpMode mode = JSINTERP_NORMAL);
Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
extern JS_REQUIRES_STACK bool
RunScript(JSContext *cx, JSScript *script, StackFrame *fp);

View File

@ -1160,7 +1160,7 @@ EvalKernel(JSContext *cx, const CallArgs &call, EvalType evalType, StackFrame *c
staticLevel = caller->script()->staticLevel + 1;
#ifdef DEBUG
jsbytecode *callerPC = caller->pc(cx);
jsbytecode *callerPC = caller->pcQuadratic(cx);
JS_ASSERT_IF(caller->isFunctionFrame(), caller->fun()->isHeavyweight());
JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
#endif

View File

@ -2070,20 +2070,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
token = CodeToken[op];
if (pc + oplen == jp->dvgfence) {
StackFrame *fp;
uint32 format, mode, type;
/*
* Rewrite non-get ops to their "get" format if the error is in
* the bytecode at pc, so we don't decompile more than the error
* expression.
*/
fp = js_GetScriptedCaller(cx, NULL);
format = cs->format;
if (((fp && pc == fp->pc(cx)) ||
FrameRegsIter iter(cx);
while (!iter.done() && !iter.fp()->isScriptFrame())
++iter;
uint32 format = cs->format;
if (((!iter.done() && pc == iter.pc()) ||
(pc == startpc && nuses != 0)) &&
format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
mode = JOF_MODE(format);
uint32 mode = JOF_MODE(format);
if (mode == JOF_NAME) {
/*
* JOF_NAME does not imply JOF_ATOM, so we must check for
@ -2091,7 +2090,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
* to JSOP_NAME.
*/
type = JOF_TYPE(format);
uint32 type = JOF_TYPE(format);
op = (type == JOF_QARG)
? JSOP_GETARG
: (type == JOF_LOCAL)

View File

@ -1647,10 +1647,9 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
}
uintN
js_FramePCToLineNumber(JSContext *cx, StackFrame *fp)
js_FramePCToLineNumber(JSContext *cx, StackFrame *fp, jsbytecode *pc)
{
return js_PCToLineNumber(cx, fp->script(),
fp->hasImacropc() ? fp->imacropc() : fp->pc(cx));
return js_PCToLineNumber(cx, fp->script(), fp->hasImacropc() ? fp->imacropc() : pc);
}
uintN
@ -1765,19 +1764,32 @@ js_GetScriptLineExtent(JSScript *script)
return 1 + lineno - script->lineno;
}
const char *
js::CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop)
namespace js {
uintN
CurrentLine(JSContext *cx)
{
StackFrame *fp = js_GetScriptedCaller(cx, NULL);
if (!fp) {
return js_FramePCToLineNumber(cx, cx->fp(), cx->regs().pc);
}
const char *
CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop)
{
FrameRegsIter iter(cx);
while (!iter.done() && !iter.fp()->isScriptFrame())
++iter;
if (iter.done()) {
*linenop = 0;
return NULL;
}
*linenop = js_FramePCToLineNumber(cx, fp);
return fp->script()->filename;
*linenop = js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
return iter.fp()->script()->filename;
}
} /* namespace js */
class DisablePrincipalsTranscoding {
JSSecurityCallbacks *callbacks;
JSPrincipalsTranscoder temp;

View File

@ -738,7 +738,7 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc);
* fp->imacpc may be non-null, indicating an active imacro.
*/
extern uintN
js_FramePCToLineNumber(JSContext *cx, js::StackFrame *fp);
js_FramePCToLineNumber(JSContext *cx, js::StackFrame *fp, jsbytecode *pc);
extern uintN
js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
@ -751,6 +751,9 @@ js_GetScriptLineExtent(JSScript *script);
namespace js {
extern uintN
CurrentLine(JSContext *cx);
/*
* This function returns the file and line number of the script currently
* executing on cx. If there is no current script executing on cx (e.g., a

View File

@ -384,6 +384,14 @@ ValueToTypeChar(const Value &v)
}
#endif
static inline uintN
CurrentPCOffset(JSContext *cx)
{
StackFrame *fp = cx->fp();
jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
return uintN(pc - fp->script()->code);
}
/* Blacklist parameters. */
@ -1636,8 +1644,8 @@ TreeFragment::initialize(JSContext* cx, SlotList *globalSlots, bool speculate)
#ifdef DEBUG
this->treeFileName = cx->fp()->script()->filename;
this->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp());
this->treePCOffset = FramePCOffset(cx, cx->fp());
this->treeLineNumber = CurrentLine(cx);
this->treePCOffset = CurrentPCOffset(cx);
#endif
this->script = cx->fp()->script();
this->gcthings.clear();
@ -2537,8 +2545,8 @@ TraceRecorder::finishAbort(const char* reason)
tree->treeLineNumber,
tree->treePCOffset,
cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()),
CurrentLine(cx),
CurrentPCOffset(cx),
reason);
#endif
Backoff(traceMonitor, (jsbytecode*) fragment->root->ip, fragment->root);
@ -4178,7 +4186,7 @@ TreevisLogExit(JSContext* cx, VMSideExit* exit)
debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\""
" LINE=%d OFFS=%d", (void*)exit, getExitName(exit->exitType),
(void*)exit->from, (void*)cx->regs().pc, cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()), FramePCOffset(cx, cx->fp()));
CurrentLine(cx), CurrentPCOffset(cx));
debug_only_print0(LC_TMTreeVis, " STACK=\"");
for (unsigned i = 0; i < exit->numStackSlots; i++)
debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->stackTypeMap()[i]));
@ -4532,8 +4540,7 @@ TraceRecorder::compile()
const char* filename = cx->fp()->script()->filename;
char* label = (char*) cx->malloc_((filename ? strlen(filename) : 7) + 16);
if (label) {
sprintf(label, "%s:%u", filename ? filename : "<stdin>",
js_FramePCToLineNumber(cx, cx->fp()));
sprintf(label, "%s:%u", filename ? filename : "<stdin>", CurrentLine(cx));
lirbuf->printer->addrNameMap->addAddrRange(fragment, sizeof(Fragment), 0, label);
cx->free_(label);
}
@ -5011,8 +5018,8 @@ TraceRecorder::closeLoop()
debug_only_printf(LC_TMMinimal,
"Recording completed at %s:%u@%u via closeLoop (FragID=%06u)\n",
cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()),
CurrentLine(cx),
CurrentPCOffset(cx),
fragment->profFragID);
debug_only_print0(LC_TMMinimal, "\n");
#endif
@ -5179,8 +5186,8 @@ TraceRecorder::endLoop(VMSideExit* exit)
debug_only_printf(LC_TMMinimal,
"Recording completed at %s:%u@%u via endLoop (FragID=%06u)\n",
cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()),
CurrentLine(cx),
CurrentPCOffset(cx),
fragment->profFragID);
debug_only_print0(LC_TMTracer, "\n");
#endif
@ -5744,8 +5751,7 @@ RecordTree(JSContext* cx, TraceMonitor* tm, TreeFragment* first,
#endif
#ifdef JS_JIT_SPEW
debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d",
(void*)f, f->ip, f->treeFileName, f->treeLineNumber,
FramePCOffset(cx, cx->fp()));
(void*)f, f->ip, f->treeFileName, f->treeLineNumber, CurrentPCOffset(cx));
debug_only_print0(LC_TMTreeVis, " STACK=\"");
for (unsigned i = 0; i < f->nStackTypes; i++)
debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[i]));
@ -5867,8 +5873,7 @@ CreateBranchFragment(JSContext* cx, TraceMonitor* tm, TreeFragment* root, VMSide
debug_only_printf(LC_TMTreeVis, "TREEVIS CREATEBRANCH ROOT=%p FRAG=%p PC=%p FILE=\"%s\""
" LINE=%d ANCHOR=%p OFFS=%d\n",
(void*)root, (void*)f, (void*)cx->regs().pc, cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()), (void*)anchor,
FramePCOffset(cx, cx->fp()));
CurrentLine(cx), (void*)anchor, CurrentPCOffset(cx));
verbose_only( tm->branches = new (*tm->dataAlloc) Seq<Fragment*>(f, tm->branches); )
f->root = root;
@ -5981,7 +5986,7 @@ AttemptToExtendTree(JSContext* cx, TraceMonitor* tm, VMSideExit* anchor, VMSideE
}
static JS_REQUIRES_STACK bool
ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount,
ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
VMSideExit** innermostNestedGuardp, VMSideExit** lrp);
static inline MonitorResult
@ -5995,7 +6000,7 @@ RecordingIfTrue(bool b)
* MONITOR_RECORDING, the recording has been aborted.
*/
JS_REQUIRES_STACK MonitorResult
TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r)
{
TraceMonitor* tm = r->traceMonitor;
@ -6025,8 +6030,8 @@ TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCall
debug_only_printf(LC_TMTracer,
"Looking for type-compatible peer (%s:%d@%d)\n",
cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()));
CurrentLine(cx),
CurrentPCOffset(cx));
// Find a matching inner tree. If none can be found, compile one.
TreeFragment* f = r->findNestedCompatiblePeer(first);
@ -6046,7 +6051,7 @@ TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCall
outerScript, outerPC, outerArgc, globalSlots));
}
AbortableRecordingStatus status = r->attemptTreeCall(f, inlineCallCount);
AbortableRecordingStatus status = r->attemptTreeCall(f);
if (status == ARECORD_CONTINUE)
return MONITOR_RECORDING;
if (status == ARECORD_ERROR) {
@ -6059,15 +6064,11 @@ TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCall
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount)
TraceRecorder::attemptTreeCall(TreeFragment* f)
{
adjustCallerTypes(f);
prepareTreeCall(f);
#ifdef DEBUG
uintN oldInlineCallCount = inlineCallCount;
#endif
JSContext *localCx = cx;
TraceMonitor *localtm = traceMonitor;
@ -6086,7 +6087,7 @@ TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount)
VMSideExit* innermostNestedGuard = NULL;
VMSideExit* lr;
bool ok = ExecuteTree(cx, traceMonitor, f, inlineCallCount, &innermostNestedGuard, &lr);
bool ok = ExecuteTree(cx, traceMonitor, f, &innermostNestedGuard, &lr);
/*
* If ExecuteTree reentered the interpreter, it may have killed |this|
@ -6120,8 +6121,6 @@ TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount)
: ARECORD_ABORTED;
}
JS_ASSERT(oldInlineCallCount == inlineCallCount);
/* Emit a call to the inner tree and continue recording the outer tree trace. */
emitTreeCall(f, lr);
return ARECORD_CONTINUE;
@ -6427,7 +6426,7 @@ FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, TreeFragment* f, uintN&
*/
JS_ALWAYS_INLINE
TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
uintN& inlineCallCount, VMSideExit** innermostNestedGuardp)
VMSideExit** innermostNestedGuardp)
: cx(cx),
traceMonitor(tm),
stackBase(tm->storage->stack()),
@ -6436,13 +6435,11 @@ TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
callstackBase(tm->storage->callstack()),
sor(callstackBase),
rp(callstackBase),
eor(callstackBase + JS_MIN(TraceNativeStorage::MAX_CALL_STACK_ENTRIES,
StackSpace::MAX_INLINE_CALLS - inlineCallCount)),
eor(callstackBase + TraceNativeStorage::MAX_CALL_STACK_ENTRIES),
lastTreeExitGuard(NULL),
lastTreeCallGuard(NULL),
rpAtLastTreeCall(NULL),
outermostTree(f),
inlineCallCountp(&inlineCallCount),
innermostNestedGuardp(innermostNestedGuardp),
#ifdef EXECUTE_TREE_TIMER
startTime(rdtsc()),
@ -6469,13 +6466,6 @@ TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
JS_ASSERT(eos == stackBase + TraceNativeStorage::MAX_NATIVE_STACK_SLOTS);
JS_ASSERT(sp < eos);
/*
* inlineCallCount has already been incremented, if being invoked from
* EnterFrame. It is okay to have a 0-frame restriction since the JIT
* might not need any frames.
*/
JS_ASSERT(inlineCallCount <= StackSpace::MAX_INLINE_CALLS);
#ifdef DEBUG
/*
* Cannot 0xCD-fill global frame since it may overwrite a bailed outer
@ -6576,7 +6566,7 @@ LeaveTree(TraceMonitor *tm, TracerState&, VMSideExit *lr);
/* Return false if the interpreter should goto error. */
static JS_REQUIRES_STACK bool
ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount,
ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
VMSideExit** innermostNestedGuardp, VMSideExit **lrp)
{
#ifdef MOZ_TRACEVIS
@ -6584,8 +6574,7 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallC
#endif
JS_ASSERT(f->root == f && f->code());
if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace() ||
inlineCallCount + f->maxCallDepth > StackSpace::MAX_INLINE_CALLS) {
if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace()) {
*lrp = NULL;
return true;
}
@ -6597,7 +6586,7 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallC
f->globalObj->shape() == f->globalShape);
/* Initialize trace state. */
TracerState state(cx, tm, f, inlineCallCount, innermostNestedGuardp);
TracerState state(cx, tm, f, innermostNestedGuardp);
double* stack = tm->storage->stack();
double* global = tm->storage->global();
JSObject* globalObj = f->globalObj;
@ -6610,8 +6599,8 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallC
AUDIT(traceTriggered);
debug_only_printf(LC_TMTracer, "entering trace at %s:%u@%u, execs: %u code: %p\n",
cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()),
CurrentLine(cx),
CurrentPCOffset(cx),
f->execs,
(void *) f->code());
@ -6655,7 +6644,7 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallC
f->treeLineNumber, prefix, (uintN)iters, f->execs,
getExitName(lr->exitType),
fp->script()->filename,
js_FramePCToLineNumber(cx, fp),
CurrentLine(cx),
js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc]);
#endif
@ -6845,12 +6834,11 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
/* Finish initializing cx->fp() and push a new cx->fp(). */
SynthesizeFrame(cx, *fi, callee);
#ifdef DEBUG
StackFrame* fp = cx->fp();
debug_only_printf(LC_TMTracer,
"synthesized deep frame for %s:%u@%u, slots=%d, fi=%p\n",
fp->script()->filename,
js_FramePCToLineNumber(cx, fp),
FramePCOffset(cx, fp),
cx->fp()->script()->filename,
CurrentLine(cx),
CurrentPCOffset(cx),
slots,
(void*)*callstack);
#endif
@ -6858,7 +6846,6 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
* Keep track of the additional frames we put on the interpreter stack
* and the native stack slots we consumed.
*/
++*state.inlineCallCountp;
++callstack;
stack += slots;
}
@ -6878,13 +6865,12 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
/* Reconstruct the frame. */
SynthesizeFrame(cx, *callstack[n], callee);
++*state.inlineCallCountp;
#ifdef DEBUG
StackFrame* fp = cx->fp();
debug_only_printf(LC_TMTracer,
"synthesized shallow frame for %s:%u@%u\n",
fp->script()->filename, js_FramePCToLineNumber(cx, fp),
FramePCOffset(cx, fp));
cx->fp()->script()->filename,
CurrentLine(cx),
CurrentPCOffset(cx));
#endif
}
@ -6937,8 +6923,8 @@ LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
"leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%s, sp=%lld, "
"calldepth=%d, cycles=%llu\n",
fp->script()->filename,
js_FramePCToLineNumber(cx, fp),
FramePCOffset(cx, fp),
CurrentLine(cx),
CurrentPCOffset(cx),
js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc],
(void*)lr,
getExitName(lr->exitType),
@ -7035,7 +7021,7 @@ TraceRecorder::assertInsideLoop()
}
JS_REQUIRES_STACK MonitorResult
RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
RecordLoopEdge(JSContext* cx, TraceMonitor* tm)
{
#ifdef MOZ_TRACEVIS
TraceVisStateObj tvso(cx, S_MONITOR);
@ -7053,7 +7039,7 @@ RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
return MONITOR_NOT_RECORDING;
}
} else {
MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount);
MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder);
JS_ASSERT((r == MONITOR_RECORDING) == (tm->recorder != NULL));
if (r == MONITOR_RECORDING || r == MONITOR_ERROR)
return r;
@ -7140,8 +7126,7 @@ RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
debug_only_printf(LC_TMTracer,
"Looking for compat peer %d@%d, from %p (ip: %p)\n",
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()), (void*)f, f->ip);
CurrentLine(cx), CurrentPCOffset(cx), (void*)f, f->ip);
uintN count;
TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, f, count);
@ -7164,7 +7149,7 @@ RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
VMSideExit* lr = NULL;
VMSideExit* innermostNestedGuard = NULL;
if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr))
if (!ExecuteTree(cx, tm, match, &innermostNestedGuard, &lr))
return MONITOR_ERROR;
if (!lr) {
@ -16711,8 +16696,7 @@ class AutoRetBlacklist
};
JS_REQUIRES_STACK TracePointAction
RecordTracePoint(JSContext* cx, TraceMonitor* tm,
uintN& inlineCallCount, bool* blacklist, bool execAllowed)
RecordTracePoint(JSContext* cx, TraceMonitor* tm, bool* blacklist, bool execAllowed)
{
StackFrame* fp = cx->fp();
jsbytecode* pc = cx->regs().pc;
@ -16736,8 +16720,7 @@ RecordTracePoint(JSContext* cx, TraceMonitor* tm,
debug_only_printf(LC_TMTracer,
"Looking for compat peer %d@%d, from %p (ip: %p)\n",
js_FramePCToLineNumber(cx, cx->fp()),
FramePCOffset(cx, cx->fp()), (void*)tree, tree->ip);
CurrentLine(cx), CurrentPCOffset(cx), (void*)tree, tree->ip);
if (tree->code() || tree->peer) {
uintN count;
@ -16753,7 +16736,7 @@ RecordTracePoint(JSContext* cx, TraceMonitor* tm,
}
/* Best case - just go and execute. */
if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr))
if (!ExecuteTree(cx, tm, match, &innermostNestedGuard, &lr))
return TPA_Error;
if (!lr)
@ -16811,7 +16794,7 @@ RecordTracePoint(JSContext* cx, TraceMonitor* tm,
JS_ASSERT(tm->recorder);
/* Locked and loaded with a recorder. Ask the interperter to go run some code. */
if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD))
if (!Interpret(cx, fp, JSINTERP_RECORD))
return TPA_Error;
JS_ASSERT(!cx->isExceptionPending());
@ -16853,7 +16836,7 @@ LoopProfile::reset()
}
MonitorResult
LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
LoopProfile::profileLoopEdge(JSContext* cx)
{
if (cx->regs().pc == top) {
debug_only_print0(LC_TMProfiler, "Profiling complete (edge)\n");
@ -16962,13 +16945,13 @@ LoopProfile::stopProfiling(JSContext *cx)
}
JS_REQUIRES_STACK TracePointAction
MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
MonitorTracePoint(JSContext *cx, bool* blacklist,
void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits)
{
TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
if (!cx->profilingEnabled)
return RecordTracePoint(cx, tm, inlineCallCount, blacklist, true);
return RecordTracePoint(cx, tm, blacklist, true);
*blacklist = false;
@ -16998,14 +16981,13 @@ MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
if (prof->profiled) {
if (prof->traceOK) {
return RecordTracePoint(cx, tm, inlineCallCount, blacklist, prof->execOK);
return RecordTracePoint(cx, tm, blacklist, prof->execOK);
} else {
return TPA_Nothing;
}
}
debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
js_FramePCToLineNumber(cx, cx->fp()));
debug_only_printf(LC_TMProfiler, "Profiling at line %d\n", CurrentLine(cx));
tm->profile = prof;
@ -17013,7 +16995,7 @@ MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
JS_THREAD_DATA(cx)->profilingCompartment = cx->compartment;
if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
if (!Interpret(cx, cx->fp(), JSINTERP_PROFILE))
return TPA_Error;
JS_ASSERT(!cx->isExceptionPending());
@ -17060,7 +17042,7 @@ LoopProfile::profileOperation(JSContext* cx, JSOp op)
if (!PCWithinLoop(fp, pc, *this)) {
debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at line %u\n",
js_FramePCToLineNumber(cx, cx->fp()));
CurrentLine(cx));
tm->profile->decide(cx);
stopProfiling(cx);
return ProfComplete;
@ -17081,7 +17063,7 @@ LoopProfile::profileOperation(JSContext* cx, JSOp op)
}
debug_only_printf(LC_TMProfiler, "Profiler: Entering inner loop at line %d\n",
js_FramePCToLineNumber(cx, cx->fp()));
CurrentLine(cx));
loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx));
}
}
@ -17442,13 +17424,13 @@ LoopProfile::decide(JSContext *cx)
}
JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode)
MonitorLoopEdge(JSContext* cx, InterpMode interpMode)
{
TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
if (interpMode == JSINTERP_PROFILE && tm->profile)
return tm->profile->profileLoopEdge(cx, inlineCallCount);
return tm->profile->profileLoopEdge(cx);
else
return RecordLoopEdge(cx, tm, inlineCallCount);
return RecordLoopEdge(cx, tm);
}
void
@ -17467,10 +17449,10 @@ AbortProfiling(JSContext *cx)
#else /* JS_METHODJIT */
JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode)
MonitorLoopEdge(JSContext* cx, InterpMode interpMode)
{
TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
return RecordLoopEdge(cx, tm, inlineCallCount);
return RecordLoopEdge(cx, tm);
}
#endif /* JS_METHODJIT */

View File

@ -794,7 +794,7 @@ public:
inline uintN count(OpKind kind) { return allOps[kind]; }
/* Called for every back edge being profiled. */
MonitorResult profileLoopEdge(JSContext* cx, uintN& inlineCallCount);
MonitorResult profileLoopEdge(JSContext* cx);
/* Called for every instruction being profiled. */
ProfileAction profileOperation(JSContext *cx, JSOp op);
@ -1539,11 +1539,9 @@ class TraceRecorder
JS_REQUIRES_STACK void determineGlobalTypes(JSValueType* typeMap);
JS_REQUIRES_STACK VMSideExit* downSnapshot(FrameInfo* downFrame);
JS_REQUIRES_STACK TreeFragment* findNestedCompatiblePeer(TreeFragment* f);
JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner,
uintN& inlineCallCount);
JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner);
static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r,
uintN& inlineCallCount);
static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r);
/* Allocators associated with this recording session. */
VMAllocator& tempAlloc() const { return *traceMonitor->tempAlloc; }
@ -1597,9 +1595,8 @@ class TraceRecorder
friend class SlotMap;
friend class DefaultSlotMap;
friend class DetermineTypesVisitor;
friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*, uintN&);
friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, uintN &inlineCallCount,
bool *blacklist);
friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*);
friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, bool *blacklist);
friend AbortResult AbortRecording(JSContext*, const char*);
friend class BoxArg;
friend void TraceMonitor::sweep(JSContext *cx);
@ -1685,14 +1682,14 @@ class TraceRecorder
#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b))
extern JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode);
MonitorLoopEdge(JSContext* cx, InterpMode interpMode);
extern JS_REQUIRES_STACK TracePointAction
RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist);
RecordTracePoint(JSContext*, bool* blacklist);
extern JS_REQUIRES_STACK TracePointAction
MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
MonitorTracePoint(JSContext*, bool* blacklist, void** traceData, uintN *traceEpoch,
uint32 *loopCounter, uint32 hits);
extern JS_REQUIRES_STACK TraceRecorder::AbortResult
AbortRecording(JSContext* cx, const char* reason);

View File

@ -1742,11 +1742,10 @@ ParseXMLSource(JSContext *cx, JSString *src)
filename = NULL;
lineno = 1;
if (!i.done()) {
StackFrame *fp = i.fp();
op = (JSOp) *i.pc();
if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
filename = fp->script()->filename;
lineno = js_FramePCToLineNumber(cx, fp);
filename = i.fp()->script()->filename;
lineno = js_FramePCToLineNumber(cx, i.fp(), i.pc());
for (endp = srcp + srclen; srcp < endp; srcp++) {
if (*srcp == '\n')
--lineno;

View File

@ -220,8 +220,7 @@ stubs::HitStackQuota(VMFrame &f)
/* Include space to push another frame. */
uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
JS_ASSERT(f.regs.sp == f.fp()->base());
StackSpace &space = f.cx->stack.space();
if (space.bumpLimitWithinQuota(NULL, f.entryfp, f.regs.sp, nvals, &f.stackLimit))
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
return;
/* Remove the current partially-constructed frame before throwing. */
@ -258,7 +257,7 @@ stubs::FixupArity(VMFrame &f, uint32 nactual)
/* Reserve enough space for a callee frame. */
StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual,
fun, fun->script(), &flags,
f.entryfp, &f.stackLimit);
&f.stackLimit);
if (!newfp) {
/*
* The PC is not coherent with the current frame, so fix it up for
@ -343,7 +342,7 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint
/* Get pointer to new frame/slots, prepare arguments. */
StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc,
newfun, newscript, &flags,
f.entryfp, &f.stackLimit);
&f.stackLimit);
if (JS_UNLIKELY(!newfp))
return false;
@ -696,7 +695,7 @@ PartialInterpret(VMFrame &f)
#endif
JSBool ok = JS_TRUE;
ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT);
ok = Interpret(cx, fp, JSINTERP_SAFEPOINT);
return ok;
}
@ -946,7 +945,6 @@ RunTracer(VMFrame &f)
entryFrame->returnValue();
bool blacklist;
uintN inlineCallCount = 0;
void **traceData;
uintN *traceEpoch;
uint32 *loopCounter;
@ -963,7 +961,7 @@ RunTracer(VMFrame &f)
loopCounter = NULL;
hits = 1;
#endif
tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
tpa = MonitorTracePoint(f.cx, &blacklist, traceData, traceEpoch,
loopCounter, hits);
JS_ASSERT(!TRACE_RECORDER(cx));

View File

@ -1035,42 +1035,12 @@ ic::NativeNew(VMFrame &f, CallICInfo *ic)
stubs::SlowNew(f, ic->frameSize.staticArgc());
}
static const unsigned MANY_ARGS = 1024;
static bool
BumpStackFull(VMFrame &f, uintN inc)
{
/* If we are not passing many args, treat this as a normal call. */
if (inc < MANY_ARGS) {
if (f.regs.sp + inc < f.stackLimit)
return true;
StackSpace &space = f.cx->stack.space();
return space.bumpLimitWithinQuota(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit);
}
/*
* The purpose of f.stackLimit is to catch over-recursion based on
* assumptions about the average frame size. 'apply' with a large number of
* arguments breaks these assumptions and can result in premature "out of
* script quota" errors. Normally, apply will go through js::Invoke, which
* effectively starts a fresh stackLimit. Here, we bump f.stackLimit,
* if necessary, to allow for this 'apply' call, and a reasonable number of
* subsequent calls, to succeed without hitting the stackLimit. In theory,
* this a recursive chain containing apply to circumvent the stackLimit.
* However, since each apply call must consume at least MANY_ARGS slots,
* this sequence will quickly reach the end of the stack and OOM.
*/
StackSpace &space = f.cx->stack.space();
return space.bumpLimit(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit);
}
static JS_ALWAYS_INLINE bool
BumpStack(VMFrame &f, uintN inc)
{
/* Fast path BumpStackFull. */
if (inc < MANY_ARGS && f.regs.sp + inc < f.stackLimit)
if (f.regs.sp + inc < f.stackLimit)
return true;
return BumpStackFull(f, inc);
return f.cx->stack.space().tryBumpLimit(f.cx, f.regs.sp, inc, &f.stackLimit);
}
/*

View File

@ -179,8 +179,7 @@ class PICStubCompiler : public BaseCompiler
void spew(const char *event, const char *op) {
#ifdef JS_METHODJIT_SPEW
JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
type, event, op, script->filename,
js_FramePCToLineNumber(cx, f.fp()));
type, event, op, script->filename, CurrentLine(cx));
#endif
}
};
@ -2055,8 +2054,7 @@ BaseIC::spew(JSContext *cx, const char *event, const char *message)
{
#ifdef JS_METHODJIT_SPEW
JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
js_CodeName[op], event, message, cx->fp()->script()->filename,
js_FramePCToLineNumber(cx, cx->fp()));
js_CodeName[op], event, message, cx->fp()->script()->filename, CurrentLine(cx));
#endif
}
@ -2240,7 +2238,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length());
JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(),
cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp()));
cx->fp()->script()->filename, CurrentLine(cx));
cx->free_(chars);
#endif

View File

@ -429,7 +429,7 @@ StackFrame::initEvalFrame(JSContext *cx, JSScript *script, StackFrame *prev, uin
scopeChain_ = &prev->scopeChain();
prev_ = prev;
prevpc_ = prev->pc(cx);
prevpc_ = prev->pcQuadratic(cx);
JS_ASSERT(!hasImacropc());
JS_ASSERT(!hasHookData());
setAnnotation(prev->annotation());
@ -767,7 +767,7 @@ StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
return true;
#else
if (end_ - from < nvals) {
js_ReportOutOfScriptQuota(maybecx);
js_ReportOverRecursed(maybecx);
return false;
}
return true;
@ -777,24 +777,22 @@ StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
inline Value *
StackSpace::getStackLimit(JSContext *cx)
{
Value *limit;
#ifdef XP_WIN
limit = commitEnd_;
#else
limit = end_;
#endif
/* See getStackLimit comment in Stack.h. */
FrameRegs &regs = cx->regs();
uintN minSpace = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
Value *sp = regs.sp;
Value *required = sp + minSpace;
Value *desired = sp + STACK_QUOTA;
#ifdef XP_WIN
if (required <= commitEnd_)
return Min(commitEnd_, desired);
if (!bumpCommit(cx, sp, minSpace))
if (regs.sp + minSpace > limit) {
js_ReportOverRecursed(cx);
return NULL;
JS_ASSERT(commitEnd_ >= required);
return commitEnd_;
#else
if (required <= end_)
return Min(end_, desired);
js_ReportOutOfScriptQuota(cx);
return NULL;
#endif
}
return limit;
}
/*****************************************************************************/
@ -819,10 +817,9 @@ struct OOMCheck
struct LimitCheck
{
StackFrame *base;
Value **limit;
LimitCheck(StackFrame *base, Value **limit) : base(base), limit(limit) {}
LimitCheck(Value **limit) : limit(limit) {}
JS_ALWAYS_INLINE bool
operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
@ -836,7 +833,7 @@ struct LimitCheck
JS_ASSERT(from < *limit);
if (*limit - from >= ptrdiff_t(nvals))
return true;
return space.bumpLimitWithinQuota(cx, base, from, nvals, limit);
return space.tryBumpLimit(cx, from, nvals, limit);
}
};
@ -896,12 +893,12 @@ ContextStack::getInlineFrame(JSContext *cx, Value *sp, uintN nactual,
JS_ALWAYS_INLINE StackFrame *
ContextStack::getInlineFrameWithinLimit(JSContext *cx, Value *sp, uintN nactual,
JSFunction *fun, JSScript *script, uint32 *flags,
StackFrame *fp, Value **limit) const
Value **limit) const
{
JS_ASSERT(isCurrentAndActive());
JS_ASSERT(cx->regs().sp == sp);
return getCallFrame(cx, sp, nactual, fun, script, flags, detail::LimitCheck(fp, limit));
return getCallFrame(cx, sp, nactual, fun, script, flags, detail::LimitCheck(limit));
}
JS_ALWAYS_INLINE void
@ -1058,39 +1055,6 @@ ContextStack::findFrameAtLevel(uintN targetLevel) const
/*****************************************************************************/
inline
FrameRegsIter::FrameRegsIter(JSContext *cx)
: cx_(cx)
{
seg_ = cx->stack.currentSegment();
if (JS_UNLIKELY(!seg_ || !seg_->isActive())) {
initSlow();
return;
}
fp_ = cx->fp();
sp_ = cx->regs().sp;
pc_ = cx->regs().pc;
return;
}
inline FrameRegsIter &
FrameRegsIter::operator++()
{
StackFrame *oldfp = fp_;
fp_ = fp_->prev();
if (!fp_)
return *this;
if (JS_UNLIKELY(oldfp == seg_->initialFrame())) {
incSlow(oldfp);
return *this;
}
pc_ = oldfp->prevpc();
sp_ = oldfp->formalArgsEnd();
return *this;
}
namespace detail {
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo

View File

@ -90,17 +90,19 @@ StackFrame::prevpcSlow()
}
jsbytecode *
StackFrame::pc(JSContext *cx, StackFrame *next)
StackFrame::pcQuadratic(JSContext *cx)
{
JS_ASSERT_IF(next, next->prev() == this);
StackSegment &seg = cx->stack.space().containingSegment(this);
FrameRegs &regs = seg.currentRegs();
/*
* This isn't just an optimization; seg->computeNextFrame(fp) is only
* defined if fp != seg->currentFrame.
*/
if (regs.fp() == this)
return regs.pc;
if (!next)
next = seg.computeNextFrame(this);
return next->prevpc();
return seg.computeNextFrame(this)->prevpc();
}
/*****************************************************************************/
@ -299,7 +301,7 @@ JS_FRIEND_API(bool)
StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
{
if (end_ - from < nvals) {
js_ReportOutOfScriptQuota(maybecx);
js_ReportOverRecursed(maybecx);
return false;
}
@ -317,7 +319,7 @@ StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
js_ReportOutOfScriptQuota(maybecx);
js_ReportOverRecursed(maybecx);
return false;
}
@ -327,44 +329,15 @@ StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
#endif
bool
StackSpace::bumpLimitWithinQuota(JSContext *maybecx, StackFrame *fp, Value *sp,
uintN nvals, Value **limit) const
StackSpace::tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit)
{
JS_ASSERT(sp >= firstUnused());
JS_ASSERT(sp + nvals >= *limit);
#ifdef XP_WIN
Value *quotaEnd = (Value *)fp + STACK_QUOTA;
if (sp + nvals < quotaEnd) {
if (!ensureSpace(NULL, sp, nvals))
goto fail;
*limit = Min(quotaEnd, commitEnd_);
return true;
}
fail:
#endif
js_ReportOverRecursed(maybecx);
return false;
}
bool
StackSpace::bumpLimit(JSContext *cx, StackFrame *fp, Value *sp,
uintN nvals, Value **limit) const
{
JS_ASSERT(*limit > base_);
JS_ASSERT(sp < *limit);
/*
* Ideally, we would only ensure space for 'nvals', not 'nvals + remain',
* since this is ~500K. However, this whole call should be a rare case: some
* script is passing a obscene number of args to 'apply' and we are just
* trying to keep the stack limit heuristic from breaking the script.
*/
Value *quota = (Value *)fp + STACK_QUOTA;
uintN remain = quota - sp;
uintN inc = nvals + remain;
if (!ensureSpace(NULL, sp, inc))
if (!ensureSpace(maybecx, from, nvals))
return false;
*limit = sp + inc;
#ifdef XP_WIN
*limit = commitEnd_;
#else
*limit = end_;
#endif
return true;
}
@ -664,31 +637,44 @@ ContextStack::notifyIfNoCodeRunning()
/*****************************************************************************/
void
FrameRegsIter::initSlow()
FrameRegsIter::FrameRegsIter(JSContext *cx)
: cx_(cx)
{
LeaveTrace(cx);
seg_ = cx->stack.currentSegment();
if (!seg_) {
fp_ = NULL;
sp_ = NULL;
pc_ = NULL;
return;
}
JS_ASSERT(seg_->isSuspended());
fp_ = seg_->suspendedFrame();
sp_ = seg_->suspendedRegs().sp;
pc_ = seg_->suspendedRegs().pc;
if (!seg_->isActive()) {
JS_ASSERT(seg_->isSuspended());
fp_ = seg_->suspendedFrame();
sp_ = seg_->suspendedRegs().sp;
pc_ = seg_->suspendedRegs().pc;
return;
}
fp_ = cx->fp();
sp_ = cx->regs().sp;
pc_ = cx->regs().pc;
return;
}
/*
* Using the invariant described in the js::StackSegment comment, we know that,
* when a pair of prev-linked stack frames are in the same segment, the
* first frame's address is the top of the prev-frame's stack, modulo missing
* arguments.
*/
void
FrameRegsIter::incSlow(StackFrame *oldfp)
FrameRegsIter &
FrameRegsIter::operator++()
{
StackFrame *oldfp = fp_;
fp_ = fp_->prev();
if (!fp_)
return *this;
if (oldfp != seg_->initialFrame()) {
pc_ = oldfp->prevpc();
sp_ = oldfp->formalArgsEnd();
return *this;
}
JS_ASSERT(oldfp == seg_->initialFrame());
JS_ASSERT(fp_ == oldfp->prev());
@ -714,6 +700,13 @@ FrameRegsIter::incSlow(StackFrame *oldfp)
f = f->prev();
}
}
return *this;
}
bool
FrameRegsIter::operator==(const FrameRegsIter &rhs) const
{
return done() == rhs.done() && (done() || fp_ == rhs.fp_);
}
/*****************************************************************************/

View File

@ -428,9 +428,20 @@ class StackFrame
/*
* Get the frame's current bytecode, assuming |this| is in |cx|.
* next is frame whose prev == this, NULL if not known or if this == cx->fp().
*
* Beware, as the name implies, pcQuadratic can lead to quadratic behavior
* in loops such as:
*
* for ( ...; fp; fp = fp->prev())
* ... fp->pcQuadratic(cx);
*
* For such situations, prefer FrameRegsIter; its amortized O(1).
*
* When I get to the bottom I go back to the top of the stack
* Where I stop and I turn and I go right back
* Till I get to the bottom and I see you again...
*/
jsbytecode *pc(JSContext *cx, StackFrame *next = NULL);
jsbytecode *pcQuadratic(JSContext *cx);
jsbytecode *prevpc() {
if (flags_ & HAS_PREVPC)
@ -1095,54 +1106,21 @@ class StackSpace
#endif
/*
* If we let infinite recursion go until it hit the end of the contiguous
* stack, it would take a long time. As a heuristic, we kill scripts which
* go deeper than MAX_INLINE_CALLS. Note: this heuristic only applies to a
* single activation of the VM. If a script reenters, the call count gets
* reset. This is ok because we will quickly hit the C recursion limit.
*/
static const size_t MAX_INLINE_CALLS = 3000;
/*
* SunSpider and v8bench have roughly an average of 9 slots per script. Our
* heuristic for a quick over-recursion check uses a generous slot count
* based on this estimate. We take this frame size and multiply it by the
* old recursion limit from the interpreter. Worst case, if an average size
* script (<=9 slots) over recurses, it'll effectively be the same as having
* increased the old inline call count to <= 5,000.
*/
static const size_t STACK_QUOTA = MAX_INLINE_CALLS * (VALUES_PER_STACK_FRAME + 18);
/*
* In the mjit, we'd like to collapse two "overflow" checks into one:
* - the MAX_INLINE_CALLS check (see above comment)
* - the stack OOM check (or, on Windows, the commit/OOM check) This
* function produces a 'limit' pointer that satisfies both these checks.
* (The STACK_QUOTA comment explains how this limit simulates checking
* MAX_INLINE_CALLS.) This limit is guaranteed to have at least enough space
* for cx->fp()->nslots() plus an extra stack frame (which is the min
* requirement for entering mjit code) or else an error is reported and NULL
* is returned. When the stack grows past the returned limit, the script may
* still be within quota, but more memory needs to be committed. This is
* handled by bumpLimitWithinQuota.
* Return a limit against which jit code can check for. This limit is not
* necessarily the end of the stack since we lazily commit stack memory on
* some platforms. Thus, when the stack limit is exceeded, the caller should
* use tryBumpLimit to attempt to increase the stack limit by committing
* more memory. If the stack is truly exhausted, tryBumpLimit will report an
* error and return NULL.
*
* An invariant of the methodjit is that there is always space to push a
* frame on top of the current frame's expression stack (which can be at
* most script->nslots deep). getStackLimit ensures that the returned limit
* does indeed have this required space and reports an error and returns
* NULL if this reserve space cannot be allocated.
*/
inline Value *getStackLimit(JSContext *cx);
/*
* Try to bump the limit, staying within |base + STACK_QUOTA|, by
* committing more pages of the contiguous stack.
* base: the frame on which execution started
* from: the current top of the stack
* nvals: requested space above 'from'
* *limit: receives bumped new limit
*/
bool bumpLimitWithinQuota(JSContext *maybecx, StackFrame *base, Value *from, uintN nvals, Value **limit) const;
/*
* Raise the given limit without considering quota.
* See comment in BumpStackFull.
*/
bool bumpLimit(JSContext *cx, StackFrame *base, Value *from, uintN nvals, Value **limit) const;
bool tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit);
/* Called during GC: mark segments, frames, and slots under firstUnused. */
void mark(JSTracer *trc);
@ -1267,7 +1245,7 @@ class ContextStack
/*
* For the five sets of stack operations below:
* - The boolean-valued functions call js_ReportOutOfScriptQuota on OOM.
* - The boolean-valued functions call js_ReportOverRecursed on OOM.
* - The "get*Frame" functions do not change any global state, they just
* check OOM and return pointers to an uninitialized frame with the
* requested missing arguments/slots. Only once the "push*Frame"
@ -1322,7 +1300,7 @@ class ContextStack
inline StackFrame *
getInlineFrameWithinLimit(JSContext *cx, Value *sp, uintN nactual,
JSFunction *fun, JSScript *script, uint32 *flags,
StackFrame *base, Value **limit) const;
Value **limit) const;
inline void pushInlineFrame(JSScript *script, StackFrame *fp, FrameRegs &regs);
inline void popInlineFrame();
@ -1424,10 +1402,12 @@ class FrameRegsIter
void incSlow(StackFrame *oldfp);
public:
inline FrameRegsIter(JSContext *cx);
FrameRegsIter(JSContext *cx);
bool done() const { return fp_ == NULL; }
inline FrameRegsIter &operator++();
FrameRegsIter &operator++();
bool operator==(const FrameRegsIter &rhs) const;
bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
StackFrame *fp() const { return fp_; }
Value *sp() const { return sp_; }