This commit is contained in:
Brendan Eich 2008-08-20 22:44:16 -07:00
commit 9e4119701e
2 changed files with 55 additions and 29 deletions

View File

@ -658,7 +658,8 @@ mergeTypeMaps(uint8** partial, unsigned* plength, uint8* complete, unsigned clen
}
TraceRecorder::TraceRecorder(JSContext* cx, GuardRecord* _anchor, Fragment* _fragment,
TreeInfo* ti, unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap)
TreeInfo* ti, unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
GuardRecord* innermostNestedGuard)
{
JS_ASSERT(!_fragment->vmprivate && ti);
@ -701,6 +702,14 @@ TraceRecorder::TraceRecorder(JSContext* cx, GuardRecord* _anchor, Fragment* _fra
/* read into registers all values on the stack and all globals we know so far */
import(treeInfo, lirbuf->sp, ngslots, callDepth, globalTypeMap, stackTypeMap);
/* If we are attached to a tree call guard, make sure the guard the inner tree exited from
is what we expect it to be. */
if (_anchor && _anchor->exit->exitType == NESTED_EXIT) {
LIns* nested_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state,
offsetof(InterpState, nestedExit)), "nestedExit");
guard(true, lir->ins2(LIR_eq, nested_ins, lir->insImmPtr(innermostNestedGuard)), NESTED_EXIT);
}
}
TraceRecorder::~TraceRecorder()
@ -1525,14 +1534,14 @@ TraceRecorder::emitTreeCall(Fragment* inner, GuardRecord* lr)
exit->typeMap, exit->typeMap + exit->numGlobalSlots);
/* Store the guard pointer in case we exit on an unexpected guard */
lir->insStorei(ret, lirbuf->state, offsetof(InterpState, nestedExit));
/* Guard that we come out of the inner tree along the same side exit we came out when
we called the inner tree at recording time. */
guard(true, lir->ins2(LIR_eq, ret, lir->insImmPtr(lr)), NESTED_EXIT);
/* Restore sp and rp to their original values (we still have them in a register). */
if (callDepth > 0) {
lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp));
lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp));
}
/* Guard that we come out of the inner tree along the same side exit we came out when
we called the inner tree at recording time. */
guard(true, lir->ins2(LIR_eq, ret, lir->insImmPtr(lr)), NESTED_EXIT);
}
int
@ -1603,11 +1612,12 @@ js_DeleteRecorder(JSContext* cx)
static bool
js_StartRecorder(JSContext* cx, GuardRecord* anchor, Fragment* f, TreeInfo* ti,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap)
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
GuardRecord* expectedInnerExit)
{
/* start recording if no exception during construction */
JS_TRACE_MONITOR(cx).recorder = new (&gc) TraceRecorder(cx, anchor, f, ti,
ngslots, globalTypeMap, stackTypeMap);
ngslots, globalTypeMap, stackTypeMap, expectedInnerExit);
if (cx->throwing) {
js_AbortRecording(cx, NULL, "setting up recorder failed");
return false;
@ -1766,13 +1776,13 @@ js_RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f)
/* recording primary trace */
return js_StartRecorder(cx, NULL, f, ti,
tm->globalSlots->length(), tm->globalTypeMap->data(),
ti->stackTypeMap.data());
ti->stackTypeMap.data(), NULL);
}
static bool
js_AttemptToExtendTree(JSContext* cx, GuardRecord* lr, Fragment* f)
js_AttemptToExtendTree(JSContext* cx, GuardRecord* lr, Fragment* f, GuardRecord* expectedInnerExit)
{
JS_ASSERT(lr->from->root == f && f->vmprivate);
JS_ASSERT(f->root == f && f->vmprivate);
debug_only_v(printf("trying to attach another branch to the tree\n");)
@ -1794,13 +1804,14 @@ js_AttemptToExtendTree(JSContext* cx, GuardRecord* lr, Fragment* f)
uint8* globalTypeMap = e->typeMap;
uint8* stackTypeMap = globalTypeMap + ngslots;
return js_StartRecorder(cx, lr, c, (TreeInfo*)f->vmprivate,
ngslots, globalTypeMap, stackTypeMap);
ngslots, globalTypeMap, stackTypeMap, expectedInnerExit);
}
return false;
}
static GuardRecord*
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount);
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount,
GuardRecord** innermostNestedGuardp);
bool
js_ContinueRecording(JSContext* cx, TraceRecorder* r, jsbytecode* oldpc, uintN& inlineCallCount)
@ -1833,20 +1844,26 @@ js_ContinueRecording(JSContext* cx, TraceRecorder* r, jsbytecode* oldpc, uintN&
r->selectCallablePeerFragment(&f) && /* is there a potentially matching peer fragment? */
r->adjustCallerTypes(f)) { /* make sure we can make our arguments fit */
r->prepareTreeCall(f);
GuardRecord* lr = js_ExecuteTree(cx, &f, inlineCallCount);
GuardRecord* innermostNestedGuard = NULL;
GuardRecord* lr = js_ExecuteTree(cx, &f, inlineCallCount, &innermostNestedGuard);
if (!lr) {
js_AbortRecording(cx, oldpc, "Couldn't call inner tree");
return false;
}
switch (lr->exit->exitType) {
case LOOP_EXIT:
/* If the inner tree exited on an unknown loop exit, grow the tree around it. */
if (innermostNestedGuard) {
js_AbortRecording(cx, oldpc, "Inner tree took different side exit, abort recording");
return js_AttemptToExtendTree(cx, innermostNestedGuard, innermostNestedGuard->from->root, lr);
}
/* emit a call to the inner tree and continue recording the outer tree trace */
r->emitTreeCall(f, lr);
return true;
case BRANCH_EXIT:
/* abort recording the outer tree, extend the inner tree */
js_AbortRecording(cx, oldpc, "Inner tree is trying to grow, abort outer recording");
return js_AttemptToExtendTree(cx, lr, lr->from);
return js_AttemptToExtendTree(cx, lr, lr->from->root, NULL);
default:
debug_only_v(printf("exit_type=%d\n", lr->exit->exitType);)
js_AbortRecording(cx, oldpc, "Inner tree not suitable for calling");
@ -1864,7 +1881,8 @@ js_ContinueRecording(JSContext* cx, TraceRecorder* r, jsbytecode* oldpc, uintN&
}
static inline GuardRecord*
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount)
js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount,
GuardRecord** innermostNestedGuardp)
{
Fragment* f = *treep;
@ -1950,6 +1968,8 @@ js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount)
/* Unwind all frames held by nested outer trees (since the innermost tree's frame which
we restore below doesn't contain such frames. */
do {
if (innermostNestedGuardp)
*innermostNestedGuardp = lr;
debug_only_v(printf("processing tree call guard %p, calldepth=%d\n", lr, lr->calldepth);)
unsigned calldepth = lr->calldepth;
if (calldepth > 0) {
@ -1973,10 +1993,6 @@ js_ExecuteTree(JSContext* cx, Fragment** treep, uintN& inlineCallCount)
lr = state.nestedExit;
}
/* At this point we have processed any outer frames around the innermost guard so
rp and callstack should match. */
JS_ASSERT(state.rp == callstack);
/* sp_adj and ip_adj are relative to the tree we exit out of, not the tree we
entered into (which might be different in the presence of nested trees). */
ti = (TreeInfo*)lr->from->root->vmprivate;
@ -2075,8 +2091,9 @@ js_LoopEdge(JSContext* cx, jsbytecode* oldpc, uintN& inlineCallCount)
/* If there is a chance that js_ExecuteTree will actually succeed, invoke it (either the
first fragment must contain some code, or at least it must have a peer fragment). */
GuardRecord* lr = NULL;
GuardRecord* innermostNestedGuard = NULL;
if (f->code() || f->peer)
lr = js_ExecuteTree(cx, &f, inlineCallCount);
lr = js_ExecuteTree(cx, &f, inlineCallCount, &innermostNestedGuard);
if (!lr) {
JS_ASSERT(!tm->recorder);
/* If we don't have compiled code for this entry point (none recorded or we trashed it),
@ -2085,12 +2102,21 @@ js_LoopEdge(JSContext* cx, jsbytecode* oldpc, uintN& inlineCallCount)
return js_RecordTree(cx, tm, f);
return false;
}
/* We successfully executed a trace, see if we should grow the tree. */
if ((lr->from->root == f) && (lr->exit->exitType == BRANCH_EXIT))
return js_AttemptToExtendTree(cx, lr, f);
/* No, this was either a loop exit or an unusual exit (i.e. out of memory/GC), so just
resume interpretation. */
return false;
/* If we exit on a branch, or on a tree call guard, try to grow the inner tree (in case
of a branch exit), or the tree nested around the tree we exited from (in case of the
tree call guard). */
SideExit* exit = lr->exit;
switch (exit->exitType) {
case BRANCH_EXIT:
return js_AttemptToExtendTree(cx, lr, lr->from->root, NULL);
case LOOP_EXIT:
if (innermostNestedGuard)
return js_AttemptToExtendTree(cx, innermostNestedGuard, innermostNestedGuard->from->root, lr);
return false;
default:
/* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
return false;
}
}
void
@ -3836,8 +3862,7 @@ TraceRecorder::record_JSOP_CALL()
*/
if (FUN_SLOW_NATIVE(fun)) {
if (fun->u.n.native == js_obj_eval) {
jsval& thisv = stackval(0 - (argc + 1));
if (JSVAL_IS_PRIMITIVE(thisv))
if (JSVAL_IS_PRIMITIVE(tval))
ABORT_TRACE("eval with primitive |this|");
jsval& arg = stackval(0 - argc);
@ -3846,7 +3871,7 @@ TraceRecorder::record_JSOP_CALL()
return true;
}
LIns* args[] = { get(&arg), get(&thisv), get(&fval), cx_ins };
LIns* args[] = { get(&arg), this_ins, get(&fval), cx_ins };
LIns* res_ins = lir->insCall(F_FastEval, args);
if (!unbox_jsval(JSVAL_STRING, res_ins))
ABORT_TRACE("unboxing non-string eval result");

View File

@ -309,7 +309,8 @@ class TraceRecorder {
public:
TraceRecorder(JSContext* cx, nanojit::GuardRecord*, nanojit::Fragment*, TreeInfo*,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap);
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
nanojit::GuardRecord* expectedInnerExit);
~TraceRecorder();
nanojit::SideExit* snapshot(nanojit::ExitType exitType);