mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 479110 - TM: avoid frequent mismatch exits. r=brendan
This commit is contained in:
parent
3c19b8b7fc
commit
fa2e6466b2
@ -1220,7 +1220,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
||||
TreeInfo* ti, unsigned stackSlots, unsigned ngslots, uint8* typeMap,
|
||||
VMSideExit* innermostNestedGuard, jsbytecode* outer)
|
||||
{
|
||||
JS_ASSERT(!_fragment->vmprivate && ti);
|
||||
JS_ASSERT(!_fragment->vmprivate && ti && cx->fp->regs->pc == (jsbytecode*)_fragment->ip);
|
||||
|
||||
/* Reset the fragment state we care about in case we got a recycled fragment. */
|
||||
_fragment->lastIns = NULL;
|
||||
@ -1241,6 +1241,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
||||
this->loop = true; /* default assumption is we are compiling a loop */
|
||||
this->wasRootFragment = _fragment == _fragment->root;
|
||||
this->outer = outer;
|
||||
this->pendingTraceableNative = NULL;
|
||||
this->generatedTraceableNative = new JSTraceableNative();
|
||||
JS_ASSERT(generatedTraceableNative);
|
||||
|
||||
@ -1754,7 +1755,7 @@ skip:
|
||||
|
||||
/* Emit load instructions onto the trace that read the initial stack state. */
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8 t,
|
||||
const char *prefix, uintN index, JSStackFrame *fp)
|
||||
{
|
||||
LIns* ins;
|
||||
@ -1767,7 +1768,7 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
ins = lir->insLoadi(base, offset);
|
||||
ins = lir->ins1(LIR_i2f, ins);
|
||||
} else {
|
||||
JS_ASSERT(t == JSVAL_BOXED || isNumber(*p) == (t == JSVAL_DOUBLE));
|
||||
JS_ASSERT_IF(t != JSVAL_BOXED, isNumber(*p) == (t == JSVAL_DOUBLE));
|
||||
if (t == JSVAL_DOUBLE) {
|
||||
ins = lir->insLoad(LIR_ldq, base, offset);
|
||||
} else if (t == JSVAL_BOOLEAN) {
|
||||
@ -1778,6 +1779,7 @@ TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
}
|
||||
checkForGlobalObjectReallocation();
|
||||
tracker.set(p, ins);
|
||||
|
||||
#ifdef DEBUG
|
||||
char name[64];
|
||||
JS_ASSERT(strlen(prefix) < 10);
|
||||
@ -1834,25 +1836,49 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne
|
||||
uint8* globalTypeMap = typeMap + stackSlots;
|
||||
unsigned length = treeInfo->nGlobalTypes();
|
||||
|
||||
/* This is potentially the typemap of the side exit and thus shorter than the tree's
|
||||
global type map. */
|
||||
if (ngslots < length)
|
||||
/*
|
||||
* This is potentially the typemap of the side exit and thus shorter than the tree's
|
||||
* global type map.
|
||||
*/
|
||||
if (ngslots < length) {
|
||||
mergeTypeMaps(&globalTypeMap/*out param*/, &ngslots/*out param*/,
|
||||
treeInfo->globalTypeMap(), length,
|
||||
(uint8*)alloca(sizeof(uint8) * length));
|
||||
}
|
||||
JS_ASSERT(ngslots == treeInfo->nGlobalTypes());
|
||||
|
||||
/* the first time we compile a tree this will be empty as we add entries lazily */
|
||||
/*
|
||||
* Check whether there are any values on the stack we have to unbox and do that first
|
||||
* before we waste any time fetching the state from the stack.
|
||||
*/
|
||||
ptrdiff_t offset = -treeInfo->nativeStackBase;
|
||||
uint8* m = typeMap;
|
||||
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
||||
if (*m == JSVAL_BOXED) {
|
||||
import(sp, offset, vp, JSVAL_BOXED, "boxed", vpnum, cx->fp);
|
||||
LIns* vp_ins = get(vp);
|
||||
unbox_jsval(*vp, vp_ins, copy(anchor));
|
||||
set(vp, vp_ins);
|
||||
}
|
||||
m++; offset += sizeof(double);
|
||||
);
|
||||
|
||||
/*
|
||||
* The first time we compile a tree this will be empty as we add entries lazily.
|
||||
*/
|
||||
uint16* gslots = treeInfo->globalSlots->data();
|
||||
uint8* m = globalTypeMap;
|
||||
m = globalTypeMap;
|
||||
FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
|
||||
JS_ASSERT(*m != JSVAL_BOXED);
|
||||
import(lirbuf->state, nativeGlobalOffset(vp), vp, *m, vpname, vpnum, NULL);
|
||||
m++;
|
||||
);
|
||||
ptrdiff_t offset = -treeInfo->nativeStackBase;
|
||||
offset = -treeInfo->nativeStackBase;
|
||||
m = typeMap;
|
||||
FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
|
||||
import(sp, offset, vp, *m, vpname, vpnum, fp);
|
||||
if (*m != JSVAL_BOXED) {
|
||||
import(sp, offset, vp, *m, vpname, vpnum, fp);
|
||||
}
|
||||
m++; offset += sizeof(double);
|
||||
);
|
||||
}
|
||||
@ -2080,7 +2106,11 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
/* Check for a return-value opcode that needs to restart at the next instruction. */
|
||||
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 &&
|
||||
JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS);
|
||||
if (resumeAfter) {
|
||||
@ -2111,13 +2141,15 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
);
|
||||
JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
|
||||
|
||||
/* If we are capturing the stack state on a specific instruction, the value on
|
||||
the top of the stack is a boxed value. */
|
||||
if (resumeAfter) {
|
||||
if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER)
|
||||
typemap[stackSlots - 1] = JSVAL_BOXED;
|
||||
/*
|
||||
* If we are currently executing a traceable native or we are attaching a second trace
|
||||
* to it, the value on top of the stack is boxed. Make a note of this in the typemap.
|
||||
*/
|
||||
if (pendingTraceableNative && (pendingTraceableNative->flags & JSTN_UNBOX_AFTER))
|
||||
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);
|
||||
regs->pc = pc - cs.length;
|
||||
} else {
|
||||
@ -2132,8 +2164,10 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
|
||||
JS_STATIC_ASSERT (sizeof(GuardRecord) + sizeof(VMSideExit) < MAX_SKIP_BYTES);
|
||||
|
||||
/* Check if we already have a matching side exit. If so use that side exit structure,
|
||||
otherwise we have to create our own. */
|
||||
/*
|
||||
* Check if we already have a matching side exit. If so use that side exit structure
|
||||
* by cloning it, otherwise we have to create our own.
|
||||
*/
|
||||
VMSideExit** exits = treeInfo->sideExits.data();
|
||||
unsigned nexits = treeInfo->sideExits.length();
|
||||
if (exitType == LOOP_EXIT) {
|
||||
@ -2141,15 +2175,8 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
VMSideExit* e = exits[n];
|
||||
if (e->pc == pc && e->imacpc == fp->imacpc &&
|
||||
!memcmp(getFullTypeMap(exits[n]), typemap, typemap_size)) {
|
||||
LIns* data = lir->skip(sizeof(GuardRecord));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
/* setup guard record structure with shared side exit */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
VMSideExit* exit = exits[n];
|
||||
rec->exit = exit;
|
||||
exit->addGuard(rec);
|
||||
AUDIT(mergedLoopExits);
|
||||
return data;
|
||||
return clone(exits[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2157,7 +2184,7 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
if (sizeof(GuardRecord) +
|
||||
sizeof(VMSideExit) +
|
||||
(stackSlots + ngslots) * sizeof(uint8) >= MAX_SKIP_BYTES) {
|
||||
/**
|
||||
/*
|
||||
* ::snapshot() is infallible in the sense that callers don't
|
||||
* expect errors; but this is a trace-aborting error condition. So
|
||||
* mangle the request to consume zero slots, and mark the tree as
|
||||
@ -2176,10 +2203,12 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
(stackSlots + ngslots) * sizeof(uint8));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
VMSideExit* exit = (VMSideExit*)(rec + 1);
|
||||
/* setup guard record structure */
|
||||
|
||||
/* Setup guard record structure. */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
/* setup side exit structure */
|
||||
|
||||
/* Setup side exit structure. */
|
||||
memset(exit, 0, sizeof(VMSideExit));
|
||||
exit->from = fragment;
|
||||
exit->calldepth = callDepth;
|
||||
@ -2206,24 +2235,77 @@ TraceRecorder::snapshot(ExitType exitType)
|
||||
return data;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK LIns*
|
||||
TraceRecorder::clone(VMSideExit* exit)
|
||||
{
|
||||
LIns* data = lir->skip(sizeof(GuardRecord));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
/* setup guard record structure with shared side exit */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
exit->addGuard(rec);
|
||||
return data;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK LIns*
|
||||
TraceRecorder::copy(VMSideExit* copy)
|
||||
{
|
||||
unsigned typemap_size = copy->numGlobalSlots + copy->numStackSlots;
|
||||
LIns* data = lir->skip(sizeof(GuardRecord) +
|
||||
sizeof(VMSideExit) +
|
||||
typemap_size * sizeof(uint8));
|
||||
GuardRecord* rec = (GuardRecord*)data->payload();
|
||||
VMSideExit* exit = (VMSideExit*)(rec + 1);
|
||||
|
||||
/* Setup guard record structure. */
|
||||
memset(rec, 0, sizeof(GuardRecord));
|
||||
rec->exit = exit;
|
||||
|
||||
/* Copy side exit structure. */
|
||||
memcpy(exit, copy, sizeof(VMSideExit) + typemap_size * sizeof(uint8));
|
||||
exit->guards = rec;
|
||||
exit->from = fragment;
|
||||
exit->target = NULL;
|
||||
|
||||
/* BIG FAT WARNING: If compilation fails, we currently don't reset the lirbuf so its safe
|
||||
to keep references to the side exits here. If we ever start rewinding those lirbufs,
|
||||
we have to make sure we purge the side exits that then no longer will be in valid
|
||||
memory. */
|
||||
if (exit->exitType == LOOP_EXIT)
|
||||
treeInfo->sideExits.add(exit);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
LIns*
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::guard(bool expected, LIns* cond, LIns* exit)
|
||||
{
|
||||
if (!cond->isCond()) {
|
||||
expected = !expected;
|
||||
cond = lir->ins_eq0(cond);
|
||||
}
|
||||
return lir->insGuard(expected ? LIR_xf : LIR_xt, cond, exit);
|
||||
#ifdef DEBUG
|
||||
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)
|
||||
and generate a side exit with type exitType to jump to if the condition does not hold. */
|
||||
JS_REQUIRES_STACK LIns*
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType)
|
||||
{
|
||||
return guard(expected, cond, snapshot(exitType));
|
||||
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
|
||||
@ -2806,19 +2888,27 @@ JS_REQUIRES_STACK void
|
||||
TraceRecorder::emitTreeCall(Fragment* inner, VMSideExit* exit)
|
||||
{
|
||||
TreeInfo* ti = (TreeInfo*)inner->vmprivate;
|
||||
|
||||
/* Invoke the inner tree. */
|
||||
LIns* args[] = { INS_CONSTPTR(inner), lirbuf->state }; /* reverse order */
|
||||
LIns* ret = lir->insCall(&js_CallTree_ci, args);
|
||||
|
||||
/* Read back all registers, in case the called tree changed any of them. */
|
||||
JS_ASSERT(!memchr(getGlobalTypeMap(exit), JSVAL_BOXED, exit->numGlobalSlots) &&
|
||||
!memchr(getStackTypeMap(exit), JSVAL_BOXED, exit->numStackSlots));
|
||||
import(ti, inner_sp_ins, exit->numStackSlots, exit->numGlobalSlots,
|
||||
exit->calldepth, getFullTypeMap(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 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, INS_CONSTPTR(exit)), NESTED_EXIT);
|
||||
/* Register us as a dependent tree of the inner tree. */
|
||||
((TreeInfo*)inner->vmprivate)->dependentTrees.addUnique(fragment->root);
|
||||
@ -6057,7 +6147,7 @@ TraceRecorder::box_jsval(jsval v, LIns*& v_ins)
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK void
|
||||
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
||||
TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
|
||||
{
|
||||
if (isNumber(v)) {
|
||||
// JSVAL_IS_NUMBER(v)
|
||||
@ -6068,7 +6158,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
||||
lir->ins2(LIR_piand, v_ins,
|
||||
INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_DOUBLE))),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
LIns* args[] = { v_ins };
|
||||
v_ins = lir->insCall(&js_UnboxDouble_ci, args);
|
||||
return;
|
||||
@ -6079,16 +6169,15 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
||||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_BOOLEAN),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
v_ins = lir->ins2i(LIR_ush, v_ins, JSVAL_TAGBITS);
|
||||
return;
|
||||
case JSVAL_OBJECT:
|
||||
if (JSVAL_IS_NULL(v)) {
|
||||
// JSVAL_NULL maps to type JSVAL_TNULL, so insist that v_ins == 0 here.
|
||||
guard(true, lir->ins_eq0(v_ins), MISMATCH_EXIT);
|
||||
guard(true, lir->ins_eq0(v_ins), exit);
|
||||
} else {
|
||||
// We must guard that v_ins has JSVAL_OBJECT tag but is not JSVAL_NULL.
|
||||
LIns* exit = snapshot(MISMATCH_EXIT);
|
||||
guard(true,
|
||||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
@ -6103,7 +6192,7 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins)
|
||||
lir->ins2i(LIR_eq,
|
||||
lir->ins2(LIR_piand, v_ins, INS_CONST(JSVAL_TAGMASK)),
|
||||
JSVAL_STRING),
|
||||
MISMATCH_EXIT);
|
||||
exit);
|
||||
v_ins = lir->ins2(LIR_piand, v_ins, INS_CONST(~JSVAL_TAGMASK));
|
||||
return;
|
||||
}
|
||||
@ -6151,10 +6240,11 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
|
||||
|
||||
bool cond = (jsuint(idx) < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && jsuint(idx) < capacity);
|
||||
if (cond) {
|
||||
LIns* exit = snapshot(exitType);
|
||||
/* Guard array length */
|
||||
LIns* exit = guard(true,
|
||||
lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
|
||||
exitType)->oprnd2();
|
||||
guard(true,
|
||||
lir->ins2(LIR_ult, idx_ins, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
|
||||
exit);
|
||||
/* dslots must not be NULL */
|
||||
guard(false,
|
||||
lir->ins_eq0(dslots_ins),
|
||||
@ -7862,13 +7952,12 @@ TraceRecorder::record_FastNativeCallComplete()
|
||||
because that would cause the interpreter to re-execute the native
|
||||
function, which might have side effects.
|
||||
|
||||
Instead, snapshot(), which is invoked from unbox_jsval() below, will see
|
||||
that we are currently parked on a traceable native's JSOP_CALL
|
||||
instruction, and it will advance the pc to restore by the length of the
|
||||
current opcode. If the native's return type is jsval, snapshot() will
|
||||
also indicate in the type map that the element on top of the stack is a
|
||||
boxed value which doesn't need to be boxed if the type guard generated
|
||||
by unbox_jsval() fails. */
|
||||
Instead, the snapshot() call below sees that we are currently parked on
|
||||
a traceable native's JSOP_CALL instruction, and it will advance the pc
|
||||
to restore by the length of the current opcode. If the native's return
|
||||
type is jsval, snapshot() will also indicate in the type map that the
|
||||
element on top of the stack is a boxed value which doesn't need to be
|
||||
boxed if the type guard generated by unbox_jsval() fails. */
|
||||
|
||||
if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS) {
|
||||
#ifdef DEBUG
|
||||
@ -7922,7 +8011,13 @@ TraceRecorder::record_FastNativeCallComplete()
|
||||
|
||||
bool ok = true;
|
||||
if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER) {
|
||||
unbox_jsval(v, v_ins);
|
||||
/*
|
||||
* If we side exit on the unboxing code due to a type change, make sure that the boxed
|
||||
* value is actually currently associated with that location, and that we are talking
|
||||
* about the top of the stack here, which is where we expected boxed values.
|
||||
*/
|
||||
JS_ASSERT(&v == &cx->fp->regs->sp[-1] && get(&v) == v_ins);
|
||||
unbox_jsval(v, v_ins, snapshot(BRANCH_EXIT));
|
||||
set(&v, v_ins);
|
||||
} else if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_NEG) {
|
||||
/* Already added i2f in functionCall. */
|
||||
@ -8035,8 +8130,13 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
||||
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
|
||||
v_ins = lir->insCall(&js_CallGetter_ci, args);
|
||||
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
|
||||
/*
|
||||
* BIG FAT WARNING: This snapshot cannot be a BRANCH_EXIT, since
|
||||
* the value to the top of the stack is not the value we unbox.
|
||||
*/
|
||||
unbox_jsval((sprop->shortid == REGEXP_SOURCE) ? JSVAL_STRING : JSVAL_BOOLEAN,
|
||||
v_ins);
|
||||
v_ins,
|
||||
snapshot(MISMATCH_EXIT));
|
||||
return true;
|
||||
}
|
||||
ABORT_TRACE("non-stub getter");
|
||||
@ -8066,7 +8166,8 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
|
||||
}
|
||||
|
||||
v_ins = stobj_get_slot(obj_ins, slot, dslots_ins);
|
||||
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins);
|
||||
unbox_jsval(STOBJ_GET_SLOT(obj, slot), v_ins, snapshot(BRANCH_EXIT));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8129,7 +8230,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. */
|
||||
v_ins = lir->insLoad(LIR_ldp, addr_ins, 0);
|
||||
unbox_jsval(*vp, v_ins);
|
||||
unbox_jsval(*vp, v_ins, snapshot(BRANCH_EXIT));
|
||||
|
||||
if (JSVAL_TAG(*vp) == JSVAL_BOOLEAN) {
|
||||
// Optimize to guard for a hole only after untagging, so we know that
|
||||
|
@ -415,7 +415,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
bool isGlobal(jsval* p) const;
|
||||
ptrdiff_t nativeGlobalOffset(jsval* p) const;
|
||||
JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(jsval* p) const;
|
||||
JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
|
||||
JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, uint8 t,
|
||||
const char *prefix, uintN index, JSStackFrame *fp);
|
||||
JS_REQUIRES_STACK void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned stackSlots,
|
||||
unsigned callDepth, unsigned ngslots, uint8* typeMap);
|
||||
@ -424,9 +424,8 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop);
|
||||
JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* guard(bool expected, nanojit::LIns* cond,
|
||||
ExitType exitType);
|
||||
nanojit::LIns* guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
|
||||
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType);
|
||||
JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
|
||||
|
||||
nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
|
||||
|
||||
@ -520,7 +519,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
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 unbox_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 bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
|
||||
nanojit::LIns* exit);
|
||||
JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
@ -561,6 +560,8 @@ public:
|
||||
|
||||
JS_REQUIRES_STACK uint8 determineSlotType(jsval* vp);
|
||||
JS_REQUIRES_STACK nanojit::LIns* snapshot(ExitType exitType);
|
||||
nanojit::LIns* clone(VMSideExit* exit);
|
||||
nanojit::LIns* copy(VMSideExit* exit);
|
||||
nanojit::Fragment* getFragment() const { return fragment; }
|
||||
TreeInfo* getTreeInfo() const { return treeInfo; }
|
||||
JS_REQUIRES_STACK void compile(JSTraceMonitor* tm);
|
||||
|
@ -48,11 +48,17 @@ function testmath(funcname, args, expected) {
|
||||
}
|
||||
testfunc.name = funcname + "(" + args + ")";
|
||||
testfunc.expected = expected;
|
||||
testfunc.jitstats = {
|
||||
recorderStarted: 1,
|
||||
recorderAborted: 0,
|
||||
traceTriggered: 1
|
||||
};
|
||||
|
||||
// Disable jitstats check. This never worked right. The actual part of the
|
||||
// loop we cared about was never traced. We traced the filler parts early
|
||||
// and then took a mismatch side exit on every subequent array read with
|
||||
// a different type (gal, discovered when fixing bug 479110).
|
||||
// testfunc.jitstats = {
|
||||
// recorderStarted: 1,
|
||||
// recorderAborted: 0,
|
||||
// traceTriggered: 1
|
||||
// };
|
||||
|
||||
test(testfunc);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user