Bug 454039 - TM: don't abort TraceRecorder::record_JSOP_SETPROP on cache miss (r=mrbkap).

This commit is contained in:
Brendan Eich 2008-09-09 22:22:52 -07:00
parent c84dcdc593
commit 4e02f9fc96
3 changed files with 117 additions and 89 deletions

View File

@ -2510,7 +2510,7 @@ js_Interpret(JSContext *cx)
# ifdef JS_TRACER
# define CHECK_RECORDER() JS_BEGIN_MACRO \
JS_ASSERT(!JS_TRACE_MONITOR(cx).recorder ^ \
JS_ASSERT(!TRACE_RECORDER(cx) ^ \
(jumpTable == recordingJumpTable)); \
JS_END_MACRO
# else
@ -2569,11 +2569,11 @@ js_Interpret(JSContext *cx)
#ifdef JS_TRACER
/* We had better not be entering the interpreter from JIT-compiled code. */
TraceRecorder *tr = NULL;
if (JS_TRACE_MONITOR(cx).onTrace) {
tr = JS_TRACE_MONITOR(cx).recorder;
JS_TRACE_MONITOR(cx).recorder = NULL;
if (JS_ON_TRACE(cx)) {
tr = TRACE_RECORDER(cx);
SET_TRACE_RECORDER(cx, NULL);
}
#endif
#endif
/* Check for too deep of a native thread stack. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
@ -2695,13 +2695,13 @@ js_Interpret(JSContext *cx)
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (jumpTable = (cx)->debugHooks->interruptHandler \
? interruptJumpTable \
: JS_TRACE_MONITOR(cx).recorder \
: TRACE_RECORDER(cx) \
? recordingJumpTable \
: normalJumpTable))
# define ENABLE_TRACER(flag) \
JS_BEGIN_MACRO \
bool flag_ = (flag); \
JS_ASSERT(flag_ == !!JS_TRACE_MONITOR(cx).recorder); \
JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
jumpTable = flag_ ? recordingJumpTable : normalJumpTable; \
JS_END_MACRO
#else /* !JS_TRACER */
@ -2715,12 +2715,11 @@ js_Interpret(JSContext *cx)
#ifdef JS_TRACER
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (switchMask = ((cx)->debugHooks->interruptHandler || \
JS_TRACE_MONITOR(cx).recorder) \
? 0 : 255))
TRACE_RECORDER(cx)) ? 0 : 255))
# define ENABLE_TRACER(flag) \
JS_BEGIN_MACRO \
bool flag_ = (flag); \
JS_ASSERT(flag_ == !!JS_TRACE_MONITOR(cx).recorder); \
JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \
switchMask = flag_ ? 0 : 255; \
JS_END_MACRO
#else /* !JS_TRACER */
@ -3022,7 +3021,7 @@ js_Interpret(JSContext *cx)
inlineCallCount--;
if (JS_LIKELY(ok)) {
#ifdef JS_TRACER
if (JS_TRACE_MONITOR(cx).recorder)
if (TRACE_RECORDER(cx))
RECORD(LeaveFrame);
#endif
JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
@ -3266,7 +3265,6 @@ js_Interpret(JSContext *cx)
* that we take into account side effects of the iterator
* call. See bug 372331.
*/
if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
goto error;
if (prop)
@ -4439,8 +4437,7 @@ js_Interpret(JSContext *cx)
* will (possibly after the first iteration) always exist
* in native object o.
*/
entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc,
kshape)];
entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
PCMETER(cache->tests++);
PCMETER(cache->settests++);
if (entry->kpc == regs.pc && entry->kshape == kshape) {
@ -4454,6 +4451,8 @@ js_Interpret(JSContext *cx)
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj)));
TRACE_2(SetPropHit, kshape, sprop);
if (scope->object == obj) {
/*
* Fastest path: the cached sprop is already
@ -4595,10 +4594,14 @@ js_Interpret(JSContext *cx)
if (!atom)
LOAD_ATOM(0);
id = ATOM_TO_JSID(atom);
if (entry
? !js_SetPropertyHelper(cx, obj, id, &rval, &entry)
: !OBJ_SET_PROPERTY(cx, obj, id, &rval)) {
goto error;
if (entry) {
if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))
goto error;
if (entry)
TRACE_1(SetPropMiss, entry);
} else {
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
goto error;
}
} while (0);
END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
@ -4908,7 +4911,7 @@ js_Interpret(JSContext *cx)
cx->fp = fp = &newifp->frame;
#ifdef JS_TRACER
if (JS_TRACE_MONITOR(cx).recorder)
if (TRACE_RECORDER(cx))
RECORD(EnterFrame);
#endif
@ -6121,6 +6124,8 @@ js_Interpret(JSContext *cx)
if (sprop->parent != scope->lastProp)
goto do_initprop_miss;
TRACE_2(SetPropHit, kshape, sprop);
/*
* Otherwise this entry must be for a direct property of
* obj, not a proto-property, and there cannot have been
@ -6186,6 +6191,8 @@ js_Interpret(JSContext *cx)
}
if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))
goto error;
if (entry)
TRACE_1(SetPropMiss, entry);
} while (0);
/* Common tail for property cache hit and miss cases. */
@ -7026,7 +7033,7 @@ js_Interpret(JSContext *cx)
JS_ASSERT(inlineCallCount == 0);
JS_ASSERT(fp->regs == &regs);
#ifdef JS_TRACER
if (JS_TRACE_MONITOR(cx).recorder)
if (TRACE_RECORDER(cx))
js_AbortRecording(cx, regs.pc, "recording out of js_Interpret");
#endif
if (JS_UNLIKELY(fp->flags & JSFRAME_YIELDING)) {
@ -7051,12 +7058,12 @@ js_Interpret(JSContext *cx)
js_SetVersion(cx, originalVersion);
--cx->interpLevel;
#ifdef JS_TRACER
#ifdef JS_TRACER
if (tr) {
JS_TRACE_MONITOR(cx).recorder = tr;
SET_TRACE_RECORDER(cx, tr);
tr->deepAbort();
}
#endif
#endif
return ok;
atom_not_defined:

View File

@ -2526,9 +2526,9 @@ js_MonitorLoopEdge(JSContext* cx, jsbytecode* oldpc, uintN& inlineCallCount)
}
bool
js_MonitorRecording(JSContext* cx)
js_MonitorRecording(TraceRecorder* tr)
{
TraceRecorder *tr = JS_TRACE_MONITOR(cx).recorder;
JSContext* cx = tr->cx;
// Clear one-shot flag used to communicate between record_JSOP_CALL and record_EnterFrame.
tr->applyingArguments = false;
@ -2540,11 +2540,12 @@ js_MonitorRecording(JSContext* cx)
}
jsbytecode* pc = cx->fp->regs->pc;
/* If we hit a break, end the loop and generate an always taken loop exit guard. For other
downward gotos (like if/else) continue recording. */
if ((*pc == JSOP_GOTO) || (*pc == JSOP_GOTOX)) {
if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
if ((sn != NULL) && (SN_TYPE(sn) == SRC_BREAK)) {
if (sn && SN_TYPE(sn) == SRC_BREAK) {
AUDIT(breakLoopExits);
tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
js_DeleteRecorder(cx);
@ -3306,6 +3307,9 @@ TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins, LIns*& ops_ins, si
bool
TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, jsuword& pcval)
{
jsbytecode* pc = cx->fp->regs->pc;
JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_SETNAME && *pc != JSOP_SETPROP);
// Mimic the interpreter's special case for dense arrays by skipping up one
// hop along the proto chain when accessing a named (not indexed) property,
// typically to find Array.prototype methods.
@ -3327,7 +3331,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
// We parameterize using offsetof and guard on match against the hook at
// the given offset in js_ObjectOps. TraceRecorder::record_JSOP_SETPROP
// guards the js_SetProperty case.
uint32 format = js_CodeSpec[*cx->fp->regs->pc].format;
uint32 format = js_CodeSpec[*pc].format;
uint32 mode = JOF_MODE(format);
// No need to guard native-ness of global object.
@ -3347,14 +3351,14 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
JSAtom* atom;
JSPropCacheEntry* entry;
PROPERTY_CACHE_TEST(cx, cx->fp->regs->pc, aobj, obj2, entry, atom);
PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom);
if (atom) {
// Miss: pre-fill the cache for the interpreter, as well as for our needs.
// FIXME: 452357 - correctly propagate exceptions into the interpreter from
// js_FindPropertyHelper, js_LookupPropertyWithFlags, and elsewhere.
jsid id = ATOM_TO_JSID(atom);
JSProperty* prop;
if (JOF_OPMODE(*cx->fp->regs->pc) == JOF_NAME) {
if (JOF_OPMODE(*pc) == JOF_NAME) {
JS_ASSERT(aobj == obj);
if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
ABORT_TRACE("failed to find name");
@ -3374,24 +3378,9 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
// via our obj2 out-parameter. If we are recording JSOP_SETNAME and
// the global it's assigning does not yet exist, create it.
obj2 = obj;
if (JSOp(*cx->fp->regs->pc) == JSOP_SETNAME) {
jsval v = JSVAL_VOID;
if (!js_SetPropertyHelper(cx, obj, id, &v, &entry))
return false;
if (!entry || !PCVAL_IS_SPROP(entry->vword))
ABORT_TRACE("can't create cacheable global for JSOP_SETNAME");
JSScopeProperty* sprop = PCVAL_TO_SPROP(entry->vword);
if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)))
ABORT_TRACE("can't create slot-ful global for JSOP_SETNAME");
pcval = SLOT_TO_PCVAL(sprop->slot);
// We are adding to the global object, so update its saved shape.
JS_ASSERT(obj == globalObj);
traceMonitor->globalShape = OBJ_SHAPE(obj);
} else {
// Use PCVAL_NULL to return "no such property" to our caller.
pcval = PCVAL_NULL;
}
// Use PCVAL_NULL to return "no such property" to our caller.
pcval = PCVAL_NULL;
return true;
}
@ -3420,10 +3409,10 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
}
} else {
#ifdef DEBUG
JSOp op = JSOp(*cx->fp->regs->pc);
JSOp op = JSOp(*pc);
ptrdiff_t pcoff = (op == JSOP_GETARGPROP) ? ARGNO_LEN :
(op == JSOP_GETLOCALPROP) ? SLOTNO_LEN : 0;
jsatomid index = js_GetIndexFromBytecode(cx, cx->fp->script, cx->fp->regs->pc, pcoff);
jsatomid index = js_GetIndexFromBytecode(cx, cx->fp->script, pc, pcoff);
JS_ASSERT(entry->kpc == (jsbytecode*) atoms[index]);
JS_ASSERT(entry->kshape == jsuword(aobj));
#endif
@ -4393,41 +4382,52 @@ TraceRecorder::record_JSOP_GETPROP()
bool
TraceRecorder::record_JSOP_SETPROP()
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (JSVAL_IS_PRIMITIVE(l))
ABORT_TRACE("primitive this for SETPROP");
JSObject* obj = JSVAL_TO_OBJECT(l);
if (obj->map->ops->setProperty != js_SetProperty)
ABORT_TRACE("non-native JSObjectOps::setProperty");
return true;
}
bool
TraceRecorder::record_SetPropHit(uint32 kshape, JSScopeProperty* sprop)
{
jsbytecode* pc = cx->fp->regs->pc;
jsval& r = stackval(-1);
jsval& l = stackval(-2);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
JSObject* obj = JSVAL_TO_OBJECT(l);
LIns* obj_ins = get(&l);
JSPropertyCache* cache = &JS_PROPERTY_CACHE(cx);
uint32 kshape = OBJ_SHAPE(obj);
jsbytecode* pc = cx->fp->regs->pc;
if (obj == globalObj) {
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)));
uint32 slot = sprop->slot;
if (!lazilyImportGlobalSlot(slot))
ABORT_TRACE("lazy import of global slot failed");
JSPropCacheEntry* entry = &cache->table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
if (entry->kpc != pc || entry->kshape != kshape)
ABORT_TRACE("cache miss");
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
LIns* r_ins = get(&r);
set(&STOBJ_GET_SLOT(obj, slot), r_ins);
JS_ASSERT(*pc != JSOP_INITPROP);
if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
set(&l, r_ins);
return true;
}
// The global object's shape is guarded at trace entry, all others need a guard here.
LIns* map_ins = lir->insLoad(LIR_ldp, obj_ins, (int)offsetof(JSObject, map));
LIns* ops_ins;
if (!map_is_native(obj->map, map_ins, ops_ins, offsetof(JSObjectOps, setProperty)))
return false;
// The global object's shape is guarded at trace entry.
if (obj != globalObj) {
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, kshape), "guard(shape)"), MISMATCH_EXIT);
}
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)), "shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, kshape), "guard(shape)"), MISMATCH_EXIT);
JSScope* scope = OBJ_SCOPE(obj);
JSScopeProperty* sprop = PCVAL_TO_SPROP(entry->vword);
if (scope->object != obj || !SCOPE_HAS_PROPERTY(scope, sprop)) {
LIns* args[] = { INS_CONSTPTR(sprop), obj_ins, cx_ins };
LIns* ok_ins = lir->insCall(F_AddProperty, args);
@ -4441,11 +4441,19 @@ TraceRecorder::record_JSOP_SETPROP()
return false;
if (!native_set(obj_ins, sprop, dslots_ins, boxed_ins))
return false;
if (*pc == JSOP_SETPROP && pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
stack(-2, v_ins);
if (*pc != JSOP_INITPROP && pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
set(&l, v_ins);
return true;
}
bool
TraceRecorder::record_SetPropMiss(JSPropCacheEntry* entry)
{
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
return record_SetPropHit(entry->kshape, PCVAL_TO_SPROP(entry->vword));
}
bool
TraceRecorder::record_JSOP_GETELEM()
{
@ -5368,8 +5376,8 @@ TraceRecorder::record_JSOP_ENDINIT()
bool
TraceRecorder::record_JSOP_INITPROP()
{
// The common code avoids stacking the RHS if op is not JSOP_SETPROP.
return record_JSOP_SETPROP();
// All the action is in record_SetPropHit.
return true;
}
bool
@ -5457,8 +5465,6 @@ TraceRecorder::record_JSOP_ITER()
bool
TraceRecorder::forInLoop(jsval* vp)
{
if (!JSVAL_IS_STRING(*vp))
ABORT_TRACE("for-in loop variable changed type from string");
jsval& iterobj_val = stackval(-1);
if (!JSVAL_IS_PRIMITIVE(iterobj_val)) {
LIns* args[] = { get(&iterobj_val), cx_ins };
@ -5467,10 +5473,11 @@ TraceRecorder::forInLoop(jsval* vp)
LIns* flag_ins = lir->ins_eq0(lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_HOLE)));
LIns* iter_ins = get(vp);
if (!box_jsval(JSVAL_STRING, iter_ins))
jsval expected = JSVAL_IS_VOID(*vp) ? JSVAL_STRING : JSVAL_TAG(*vp);
if (!box_jsval(expected, iter_ins))
return false;
iter_ins = lir->ins_choose(flag_ins, v_ins, iter_ins, true);
if (!unbox_jsval(JSVAL_STRING, iter_ins))
if (!unbox_jsval(expected, iter_ins))
return false;
set(vp, iter_ins);
stack(0, flag_ins);
@ -5554,7 +5561,6 @@ TraceRecorder::record_JSOP_BINDNAME()
bool
TraceRecorder::record_JSOP_SETNAME()
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
@ -5564,16 +5570,9 @@ TraceRecorder::record_JSOP_SETNAME()
*/
JSObject* obj = JSVAL_TO_OBJECT(l);
if (obj != cx->fp->scopeChain || obj != globalObj)
return false;
ABORT_TRACE("JSOP_SETNAME left operand is not the global object");
jsval* vp;
if (!name(vp))
return false;
LIns* r_ins = get(&r);
set(vp, r_ins);
if (cx->fp->regs->pc[JSOP_SETNAME_LENGTH] != JSOP_POP)
stack(-2, r_ins);
// The rest of the work is in record_SetPropHit.
return true;
}

View File

@ -334,7 +334,7 @@ class TraceRecorder {
void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
public:
friend bool js_MonitorRecording(JSContext* cx);
friend bool js_MonitorRecording(TraceRecorder* tr);
TraceRecorder(JSContext* cx, nanojit::GuardRecord*, nanojit::Fragment*, TreeInfo*,
unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
@ -358,6 +358,8 @@ public:
bool record_EnterFrame();
bool record_LeaveFrame();
bool record_SetPropHit(uint32 kshape, JSScopeProperty* sprop);
bool record_SetPropMiss(JSPropCacheEntry* entry);
void deepAbort() { deepAborted = true; }
bool wasDeepAborted() { return deepAborted; }
@ -369,23 +371,43 @@ public:
};
#define TRACING_ENABLED(cx) JS_HAS_OPTION(cx, JSOPTION_JIT)
#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder)
#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
#define RECORD(x) \
// See jsinterp.cpp for the ENABLE_TRACER definition.
#define RECORD_ARGS(x,args) \
JS_BEGIN_MACRO \
TraceRecorder* r = JS_TRACE_MONITOR(cx).recorder; \
if (!js_MonitorRecording(cx)) { \
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
if (!js_MonitorRecording(tr_)) \
ENABLE_TRACER(0); \
} else if (!r->record_##x()) { \
else \
TRACE_ARGS_(tr_,x,args); \
JS_END_MACRO
#define TRACE_ARGS_(tr,x,args) \
JS_BEGIN_MACRO \
if (!tr->record_##x args) { \
js_AbortRecording(cx, NULL, #x); \
ENABLE_TRACER(0); \
} \
JS_END_MACRO
#define TRACE_ARGS(x,args) \
JS_BEGIN_MACRO \
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
if (tr_) \
TRACE_ARGS_(tr_, x, args); \
JS_END_MACRO
#define RECORD(x) RECORD_ARGS(x, ())
#define TRACE_1(x,a) TRACE_ARGS(x, (a))
#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b))
extern bool
js_MonitorLoopEdge(JSContext* cx, jsbytecode* oldpc, uintN& inlineCallCount);
extern bool
js_MonitorRecording(JSContext* cx);
js_MonitorRecording(TraceRecorder *tr);
extern void
js_AbortRecording(JSContext* cx, jsbytecode* abortpc, const char* reason);