This commit is contained in:
Andreas Gal 2009-03-17 21:51:19 -07:00
commit 09195e59c7
3 changed files with 46 additions and 96 deletions

View File

@ -1201,7 +1201,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
TreeInfo* ti, unsigned stackSlots, unsigned ngslots, uint8* typeMap, TreeInfo* ti, unsigned stackSlots, unsigned ngslots, uint8* typeMap,
VMSideExit* innermostNestedGuard, jsbytecode* outer) VMSideExit* innermostNestedGuard, jsbytecode* outer)
{ {
JS_ASSERT(!_fragment->vmprivate && ti && cx->fp->regs->pc == (jsbytecode*)_fragment->ip); JS_ASSERT(!_fragment->vmprivate && ti);
this->cx = cx; this->cx = cx;
this->traceMonitor = &JS_TRACE_MONITOR(cx); this->traceMonitor = &JS_TRACE_MONITOR(cx);
@ -1219,9 +1219,6 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
this->terminate = false; this->terminate = false;
this->wasRootFragment = _fragment == _fragment->root; this->wasRootFragment = _fragment == _fragment->root;
this->outer = outer; this->outer = outer;
this->pendingTraceableNative = NULL;
this->pendingBoxedValue = JSVAL_HOLE;
this->pendingBoxedIns = NULL;
debug_only_v(printf("recording starting from %s:%u@%u\n", debug_only_v(printf("recording starting from %s:%u@%u\n",
ti->treeFileName, ti->treeLineNumber, ti->treePCOffset);) ti->treeFileName, ti->treeLineNumber, ti->treePCOffset);)
@ -1254,14 +1251,6 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
/* read into registers all values on the stack and all globals we know so far */ /* read into registers all values on the stack and all globals we know so far */
import(treeInfo, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap); import(treeInfo, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap);
/* unbox any boxed values we imported as jsvals */
if (pendingBoxedIns) {
JS_ASSERT(fragment != fragment->root);
unbox_jsval(pendingBoxedValue, pendingBoxedIns, snapshot(BRANCH_EXIT));
pendingBoxedValue = JSVAL_NULL;
pendingBoxedIns = NULL;
}
if (fragment == fragment->root) { if (fragment == fragment->root) {
/* /*
* We poll the operation callback request flag. It is updated asynchronously whenever * We poll the operation callback request flag. It is updated asynchronously whenever
@ -1749,7 +1738,7 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
ins = lir->insLoadi(base, offset); ins = lir->insLoadi(base, offset);
ins = lir->ins1(LIR_i2f, ins); ins = lir->ins1(LIR_i2f, ins);
} else { } else {
JS_ASSERT_IF(t != JSVAL_BOXED, isNumber(*p) == (t == JSVAL_DOUBLE)); JS_ASSERT(t == JSVAL_BOXED || isNumber(*p) == (t == JSVAL_DOUBLE));
if (t == JSVAL_DOUBLE) { if (t == JSVAL_DOUBLE) {
ins = lir->insLoad(LIR_ldq, base, offset); ins = lir->insLoad(LIR_ldq, base, offset);
} else if (t == JSVAL_BOOLEAN) { } else if (t == JSVAL_BOOLEAN) {
@ -1760,17 +1749,6 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
} }
checkForGlobalObjectReallocation(); checkForGlobalObjectReallocation();
tracker.set(p, ins); tracker.set(p, ins);
/*
* If we are attaching a branch to an unbox operation that failed, make a note to unbox
* the value as soon as we imported everything.
*/
if (t == JSVAL_BOXED) {
JS_ASSERT(!pendingBoxedIns);
pendingBoxedValue = *p;
pendingBoxedIns = ins;
}
#ifdef DEBUG #ifdef DEBUG
char name[64]; char name[64];
JS_ASSERT(strlen(prefix) < 10); JS_ASSERT(strlen(prefix) < 10);
@ -2124,14 +2102,9 @@ TraceRecorder::snapshot(ExitType exitType)
/* Check for a return-value opcode that needs to restart at the next instruction. */ /* Check for a return-value opcode that needs to restart at the next instruction. */
const JSCodeSpec& cs = js_CodeSpec[*pc]; const JSCodeSpec& cs = js_CodeSpec[*pc];
/* /* WARNING: don't return before restoring the original pc if (resumeAfter). */
* When calling a _FAIL native, make the snapshot's pc point to the next
* instruction after the CALL or APPLY. Even on failure, a _FAIL native must not
* be called again from the interpreter.
*/
bool resumeAfter = (pendingTraceableNative && bool resumeAfter = (pendingTraceableNative &&
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS); JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS);
if (resumeAfter) { if (resumeAfter) {
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY); JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY);
pc += cs.length; pc += cs.length;
@ -2160,15 +2133,13 @@ TraceRecorder::snapshot(ExitType exitType)
); );
JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots); JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
/* /* If we are capturing the stack state on a specific instruction, the value on
* If we are currently executing a traceable native or we are attaching a second trace the top of the stack is a boxed value. */
* to it, the value on top of the stack is boxed. Make a note of this in the typemap. if (resumeAfter) {
*/ if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER)
if ((pendingTraceableNative && (pendingTraceableNative->flags & JSTN_UNBOX_AFTER)) || pendingBoxedIns)
typemap[stackSlots - 1] = JSVAL_BOXED; typemap[stackSlots - 1] = JSVAL_BOXED;
/* Now restore the the original pc (after which early returns are ok). */ /* Now restore the the original pc (after which early returns are ok). */
if (resumeAfter) {
MUST_FLOW_LABEL(restore_pc); MUST_FLOW_LABEL(restore_pc);
regs->pc = pc - cs.length; regs->pc = pc - cs.length;
} else { } else {
@ -2259,34 +2230,22 @@ TraceRecorder::snapshot(ExitType exitType)
/* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected) /* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected)
and using the supplied side exit if the conditon doesn't hold. */ and using the supplied side exit if the conditon doesn't hold. */
void LIns*
TraceRecorder::guard(bool expected, LIns* cond, LIns* exit) TraceRecorder::guard(bool expected, LIns* cond, LIns* exit)
{ {
if (!cond->isCond()) { if (!cond->isCond()) {
expected = !expected; expected = !expected;
cond = lir->ins_eq0(cond); cond = lir->ins_eq0(cond);
} }
#ifdef DEBUG return lir->insGuard(expected ? LIR_xf : LIR_xt, cond, exit);
LIns* guard =
#endif
lir->insGuard(expected ? LIR_xf : LIR_xt, cond, exit);
#ifdef DEBUG
if (guard) {
GuardRecord* lr = guard->record();
VMSideExit* e = (VMSideExit*)lr->exit;
debug_only_v(printf(" lr=%p exitType=%d\n", (SideExit*)e, e->exitType);)
} else {
debug_only_v(printf(" redundant guard, eliminated\n");)
}
#endif
} }
/* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected) /* Emit a guard for condition (cond), expecting to evaluate to boolean result (expected)
and generate a side exit with type exitType to jump to if the condition does not hold. */ and generate a side exit with type exitType to jump to if the condition does not hold. */
JS_REQUIRES_STACK void JS_REQUIRES_STACK LIns*
TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType) TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType)
{ {
guard(expected, cond, snapshot(exitType)); return guard(expected, cond, snapshot(exitType));
} }
/* Try to match the type of a slot to type t. checkType is used to verify that the type of /* Try to match the type of a slot to type t. checkType is used to verify that the type of
@ -2682,12 +2641,14 @@ TraceRecorder::closeLoop(JSTraceMonitor* tm, bool& demote)
((TreeInfo*)peer->vmprivate)->dependentTrees.addUnique(fragment->root); ((TreeInfo*)peer->vmprivate)->dependentTrees.addUnique(fragment->root);
treeInfo->linkedTrees.addUnique(peer); treeInfo->linkedTrees.addUnique(peer);
} }
compile(tm);
} else { } else {
exit->target = fragment->root; exit->target = fragment->root;
fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), exitIns); fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), exitIns);
compile(tm);
} }
compile(tm);
if (fragmento->assm()->error() != nanojit::None) if (fragmento->assm()->error() != nanojit::None)
return false; return false;
@ -2881,7 +2842,6 @@ TraceRecorder::emitTreeCall(Fragment* inner, VMSideExit* exit)
/* Read back all registers, in case the called tree changed any of them. */ /* Read back all registers, in case the called tree changed any of them. */
import(ti, inner_sp_ins, exit->numStackSlots, exit->numGlobalSlots, import(ti, inner_sp_ins, exit->numStackSlots, exit->numGlobalSlots,
exit->calldepth, getFullTypeMap(exit)); exit->calldepth, getFullTypeMap(exit));
JS_ASSERT(!pendingBoxedIns);
/* Restore sp and rp to their original values (we still have them in a register). */ /* Restore sp and rp to their original values (we still have them in a register). */
if (callDepth > 0) { if (callDepth > 0) {
lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp)); lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp));
@ -5942,7 +5902,7 @@ TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
} }
JS_REQUIRES_STACK void JS_REQUIRES_STACK void
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit) TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
{ {
if (isNumber(v)) { if (isNumber(v)) {
// JSVAL_IS_NUMBER(v) // JSVAL_IS_NUMBER(v)
@ -5953,7 +5913,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
lir->ins2(LIR_piand, v_ins, lir->ins2(LIR_piand, v_ins,
INS_CONST(JSVAL_TAGMASK)), INS_CONST(JSVAL_TAGMASK)),
JSVAL_DOUBLE))), JSVAL_DOUBLE))),
exit); MISMATCH_EXIT);
LIns* args[] = { v_ins }; LIns* args[] = { v_ins };
v_ins = lir->insCall(&js_UnboxDouble_ci, args); v_ins = lir->insCall(&js_UnboxDouble_ci, args);
return; return;
@ -5964,15 +5924,16 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
lir->ins2i(LIR_eq, lir->ins2i(LIR_eq,
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)), lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
JSVAL_BOOLEAN), JSVAL_BOOLEAN),
exit); MISMATCH_EXIT);
v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS); v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
return; return;
case JSVAL_OBJECT: case JSVAL_OBJECT:
if (JSVAL_IS_NULL(v)) { if (JSVAL_IS_NULL(v)) {
// JSVAL_NULL maps to type JSVAL_TNULL, so insist that v_ins == 0 here. // JSVAL_NULL maps to type JSVAL_TNULL, so insist that v_ins == 0 here.
guard(true, lir->ins_eq0(v_ins), exit); guard(true, lir->ins_eq0(v_ins), MISMATCH_EXIT);
} else { } else {
// We must guard that v_ins has JSVAL_OBJECT tag but is not JSVAL_NULL. // We must guard that v_ins has JSVAL_OBJECT tag but is not JSVAL_NULL.
LIns* exit = snapshot(MISMATCH_EXIT);
guard(true, guard(true,
lir->ins2i(LIR_eq, lir->ins2i(LIR_eq,
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)), lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
@ -5987,7 +5948,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
lir->ins2i(LIR_eq, lir->ins2i(LIR_eq,
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)), lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
JSVAL_STRING), JSVAL_STRING),
exit); MISMATCH_EXIT);
v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK)); v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
return; return;
} }
@ -6035,11 +5996,10 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity); bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity);
if (cond) { if (cond) {
LIns* exit = snapshot(exitType);
/* Guard array length */ /* Guard array length */
guard(true, LIns* exit = guard(true,
lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
exit); exitType)->oprnd2();
/* dslots must not be NULL */ /* dslots must not be NULL */
guard(false, guard(false,
lir->ins_eq0(dslots_ins), lir->ins_eq0(dslots_ins),
@ -7613,25 +7573,23 @@ TraceRecorder::record_FastNativeCallComplete()
because that would cause the interpreter to re-execute the native because that would cause the interpreter to re-execute the native
function, which might have side effects. function, which might have side effects.
Instead, the snapshot() call below sees that we are currently parked on Instead, snapshot(), which is invoked from unbox_jsval() below, will see
a traceable native's JSOP_CALL instruction, and it will advance the pc that we are currently parked on a traceable native's JSOP_CALL
to restore by the length of the current opcode. If the native's return instruction, and it will advance the pc to restore by the length of the
type is jsval, snapshot() will also indicate in the type map that the current opcode. If the native's return type is jsval, snapshot() will
element on top of the stack is a boxed value which doesn't need to be also indicate in the type map that the element on top of the stack is a
boxed if the type guard generated by unbox_jsval() fails. */ boxed value which doesn't need to be boxed if the type guard generated
by unbox_jsval() fails. */
LIns* exit = NULL;
if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS) { if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS) {
#ifdef DEBUG #ifdef DEBUG
// Keep cx->bailExit null when it's invalid. // Keep cx->bailExit null when it's invalid.
lir->insStorei(INS_CONSTPTR(NULL), cx_ins, (int) offsetof(JSContext, bailExit)); lir->insStorei(INS_CONSTPTR(NULL), cx_ins, (int) offsetof(JSContext, bailExit));
#endif #endif
exit = snapshot(STATUS_EXIT);
guard(true, guard(true,
lir->ins_eq0( lir->ins_eq0(
lir->insLoad(LIR_ld, cx_ins, (int) offsetof(JSContext, builtinStatus))), lir->insLoad(LIR_ld, cx_ins, (int) offsetof(JSContext, builtinStatus))),
exit); STATUS_EXIT);
} }
JS_ASSERT(*cx->fp->regs->pc == JSOP_CALL || JS_ASSERT(*cx->fp->regs->pc == JSOP_CALL ||
@ -7642,7 +7600,7 @@ TraceRecorder::record_FastNativeCallComplete()
bool ok = true; bool ok = true;
if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER) { if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER) {
unbox_jsval(v, v_ins, exit ? exit : snapshot(BRANCH_EXIT)); unbox_jsval(v, v_ins);
set(&v, v_ins); set(&v, v_ins);
} else if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_NEG) { } else if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_NEG) {
/* Already added i2f in functionCall. */ /* Already added i2f in functionCall. */
@ -7758,8 +7716,7 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
v_ins = lir->insCall(&js_CallGetter_ci, args); v_ins = lir->insCall(&js_CallGetter_ci, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT); guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN, unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
v_ins, v_ins);
snapshot(MISMATCH_EXIT));
JS_ASSERT(cs.ndefs == 1); JS_ASSERT(cs.ndefs == 1);
stack(-cs.nuses, v_ins); stack(-cs.nuses, v_ins);
return true; return true;
@ -7791,7 +7748,7 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
} }
v_ins = stobj_get_slot(obj_ins, slot, dslots_ins); v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins, snapshot(BRANCH_EXIT)); unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins);
return true; return true;
} }
@ -7854,7 +7811,7 @@ TraceRecorder::elem(jsval& oval, jsval& idx, jsval*& vp, LIns*& v_ins, LIns*& ad
/* Load the value and guard on its type to unbox it. */ /* Load the value and guard on its type to unbox it. */
v_ins = lir->insLoad(LIR_ldp, addr_ins, 0); v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
unbox_jsval(*vp, v_ins, snapshot(BRANCH_EXIT)); unbox_jsval(*vp, v_ins);
if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) { if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
// Optimize to guard for a hole only after untagging, so we know that // Optimize to guard for a hole only after untagging, so we know that

View File

@ -412,8 +412,6 @@ class TraceRecorder : public avmplus::GCObject {
Queue<jsbytecode*> cfgMerges; Queue<jsbytecode*> cfgMerges;
jsval* global_dslots; jsval* global_dslots;
JSTraceableNative* pendingTraceableNative; JSTraceableNative* pendingTraceableNative;
jsval pendingBoxedValue;
nanojit::LIns* pendingBoxedIns;
bool terminate; bool terminate;
jsbytecode* terminate_pc; jsbytecode* terminate_pc;
jsbytecode* terminate_imacpc; jsbytecode* terminate_imacpc;
@ -433,8 +431,9 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop); JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop);
JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot); JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType); JS_REQUIRES_STACK nanojit::LIns* guard(bool expected, nanojit::LIns* cond,
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit); ExitType exitType);
nanojit::LIns* guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
nanojit::LIns* addName(nanojit::LIns* ins, const char* name); nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
@ -528,7 +527,7 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK bool getThis(nanojit::LIns*& this_ins); JS_REQUIRES_STACK bool getThis(nanojit::LIns*& this_ins);
JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins); JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins, nanojit::LIns* exit); JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins);
JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp, JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
nanojit::LIns* exit); nanojit::LIns* exit);
JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins, JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,

View File

@ -48,17 +48,11 @@ function testmath(funcname, args, expected) {
} }
testfunc.name = funcname + "(" + args + ")"; testfunc.name = funcname + "(" + args + ")";
testfunc.expected = expected; testfunc.expected = expected;
testfunc.jitstats = {
// Disable jitstats check. This never worked right. The actual part of the recorderStarted: 1,
// loop we cared about was never traced. We traced the filler parts early recorderAborted: 0,
// and then took a mismatch side exit on every subequent array read with traceTriggered: 1
// a different type (gal, discovered when fixing bug 479110). };
// testfunc.jitstats = {
// recorderStarted: 1,
// recorderAborted: 0,
// traceTriggered: 1
// };
test(testfunc); test(testfunc);
} }