--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2505,17 +2505,17 @@ js_Interpret(JSContext *cx)
# include "jsopcode.tbl"
# undef OPDEF
};
METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */
# 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
# define CHECK_RECORDER() ((void)0)
# endif
# define DO_OP() JS_BEGIN_MACRO \
CHECK_RECORDER(); \
@@ -2564,21 +2564,21 @@ js_Interpret(JSContext *cx)
# define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
# define END_EMPTY_CASES goto advance_pc_by_one;
#endif /* !JS_THREADED_INTERP */
#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);
rt = cx->runtime;
/* Set registerized frame pointer and derived script pointer. */
fp = cx->fp;
@@ -2690,42 +2690,41 @@ js_Interpret(JSContext *cx)
* not have to reload it each time through the interpreter loop -- we hope
* the compiler can keep it in a register when it is non-null.
*/
#if JS_THREADED_INTERP
#ifdef JS_TRACER
# 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 */
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (jumpTable = (cx)->debugHooks->interruptHandler \
? interruptJumpTable \
: normalJumpTable))
# define ENABLE_TRACER(flag) ((void)0)
#endif /* !JS_TRACER */
#else /* !JS_THREADED_INTERP */
#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 */
# define LOAD_INTERRUPT_HANDLER(cx) \
((void) (switchMask = ((cx)->debugHooks->interruptHandler \
? 0 : 255)))
# define ENABLE_TRACER(flag) ((void)0)
#endif /* !JS_TRACER */
@@ -3017,17 +3016,17 @@ js_Interpret(JSContext *cx)
/* Restore the calling script's interpreter registers. */
script = fp->script;
atoms = script->atomMap.vector;
/* Resume execution in the calling frame. */
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);
len = JSOP_CALL_LENGTH;
DO_NEXT_OP(len);
}
goto error;
}
@@ -3261,17 +3260,16 @@ js_Interpret(JSContext *cx)
default:
JS_ASSERT(op == JSOP_FORNAME);
/*
* We find property here after the iterator call to ensure
* 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)
OBJ_DROP_PROPERTY(cx, obj2, prop);
set_for_property:
/* Set the variable obj[id] to refer to rval. */
fp->flags |= JSFRAME_ASSIGNING;
@@ -4434,31 +4432,32 @@ js_Interpret(JSContext *cx)
* of property additions. And second:
*
* o.p = x;
*
* in a frequently executed method or loop body, where p
* 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) {
JSScope *scope;
JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj);
if (scope->shape == kshape) {
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
sprop = PCVAL_TO_SPROP(entry->vword);
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
* in scope. Just NATIVE_SET and break to get
* out of the do-while(0).
*/
if (sprop == scope->lastProp ||
SCOPE_HAS_PROPERTY(scope, sprop)) {
@@ -4590,20 +4589,24 @@ js_Interpret(JSContext *cx)
if (obj == obj2 && !PCVAL_IS_OBJECT(entry->vword))
break;
}
}
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);
BEGIN_CASE(JSOP_GETELEM)
/* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
lval = FETCH_OPND(-2);
rval = FETCH_OPND(-1);
@@ -4903,17 +4906,17 @@ js_Interpret(JSContext *cx)
newifp->callerRegs = regs;
fp->regs = &newifp->callerRegs;
regs.sp = newsp;
regs.pc = script->code;
newifp->frame.regs = ®s;
cx->fp = fp = &newifp->frame;
#ifdef JS_TRACER
- if (JS_TRACE_MONITOR(cx).recorder)
+ if (TRACE_RECORDER(cx))
RECORD(EnterFrame);
#endif
inlineCallCount++;
JS_RUNTIME_METER(rt, inlineCalls);
#ifdef INCLUDE_MOZILLA_DTRACE
/* DTrace function entry, inlines */
@@ -6116,16 +6119,18 @@ js_Interpret(JSContext *cx)
/*
* Detect a repeated property name and force a miss to
* share the strict warning code and cope with complexity
* managed by js_AddScopeProperty.
*/
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
* any deletions of prior properties.
*/
JS_ASSERT(PCVCAP_MAKE(sprop->shape, 0, 0) == entry->vcap);
JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));
JS_ASSERT(!scope->table ||
@@ -6181,16 +6186,18 @@ js_Interpret(JSContext *cx)
/* Set the property named by obj[id] to rval. */
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER,
NULL, NULL)) {
goto error;
}
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. */
regs.sp--;
END_CASE(JSOP_INITPROP);
BEGIN_CASE(JSOP_INITELEM)
/* Pop the element's value into rval. */
@@ -7021,17 +7028,17 @@ js_Interpret(JSContext *cx)
*
* We must not be in an inline frame. The check above ensures that for the
* error case and for a normal return, the code jumps directly to parent's
* frame pc.
*/
JS_ASSERT(inlineCallCount == 0);
JS_ASSERT(fp->regs == ®s);
#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)) {
JSGenerator *gen;
gen = FRAME_TO_GENERATOR(fp);
gen->savedRegs = regs;
gen->frame.regs = &gen->savedRegs;
@@ -7046,22 +7053,22 @@ js_Interpret(JSContext *cx)
/* Undo the remaining effects committed on entry to js_Interpret. */
if (script->staticDepth < JS_DISPLAY_SIZE)
cx->display[script->staticDepth] = fp->displaySave;
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
if (cx->version == currentVersion && currentVersion != originalVersion)
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:
{
const char *printable;
printable = js_AtomToPrintableString(cx, atom);
if (printable)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2521,35 +2521,36 @@ js_MonitorLoopEdge(JSContext* cx, jsbyte
return false;
default:
/* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
return false;
}
}
bool
-js_MonitorRecording(JSContext* cx)
-{
- TraceRecorder *tr = JS_TRACE_MONITOR(cx).recorder;
+js_MonitorRecording(TraceRecorder* tr)
+{
+ JSContext* cx = tr->cx;
// Clear one-shot flag used to communicate between record_JSOP_CALL and record_EnterFrame.
tr->applyingArguments = false;
// Process deepAbort() requests now.
if (tr->wasDeepAborted()) {
js_AbortRecording(cx, NULL, "deep abort requested");
return false;
}
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);
return false; /* done recording */
}
}
/* An explicit return from callDepth 0 should end the loop, not abort it. */
@@ -3301,16 +3302,19 @@ TraceRecorder::map_is_native(JSObjectMap
#undef OP
ABORT_TRACE("non-native map");
}
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.
JSObject* aobj = obj;
if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
aobj = OBJ_GET_PROTO(cx, obj);
obj_ins = stobj_get_fslot(obj_ins, JSSLOT_PROTO);
}
@@ -3322,17 +3326,17 @@ TraceRecorder::test_property_cache(JSObj
// (newObjectMap == js_ObjectOps.newObjectMap) which is required to use
// native objects (those whose maps are scopes), or even more narrow
// conditions required because the cache miss case will call a particular
// object-op (js_GetProperty, js_SetProperty).
//
// 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.
JS_ASSERT(OBJ_IS_NATIVE(globalObj));
if (aobj != globalObj) {
size_t op_offset = 0;
if (mode == JOF_PROP || mode == JOF_VARPROP) {
JS_ASSERT(!(format & JOF_SET));
@@ -3342,24 +3346,24 @@ TraceRecorder::test_property_cache(JSObj
}
if (!map_is_native(aobj->map, map_ins, ops_ins, op_offset))
return false;
}
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");
} else {
int protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, 0, &obj2, &prop);
if (protoIndex < 0)
ABORT_TRACE("failed to lookup property");
@@ -3369,34 +3373,19 @@ TraceRecorder::test_property_cache(JSObj
}
}
if (!prop) {
// Propagate obj from js_FindPropertyHelper to record_JSOP_BINDNAME
// 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;
}
OBJ_DROP_PROPERTY(cx, obj2, prop);
if (!entry)
ABORT_TRACE("failed to fill property cache");
}
@@ -3415,20 +3404,20 @@ TraceRecorder::test_property_cache(JSObj
if (aobj != globalObj) {
LIns* shape_ins = addName(lir->insLoad(LIR_ld, map_ins, offsetof(JSScope, shape)),
"shape");
guard(true, addName(lir->ins2i(LIR_eq, shape_ins, entry->kshape), "guard(kshape)"),
MISMATCH_EXIT);
}
} 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
if (aobj != globalObj) {
guard(true, addName(lir->ins2i(LIR_eq, obj_ins, entry->kshape), "guard(kobj)"),
MISMATCH_EXIT);
}
}
@@ -4388,70 +4377,89 @@ bool
TraceRecorder::record_JSOP_GETPROP()
{
return getProp(stackval(-1));
}
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");
-
- LIns* obj_ins = get(&l);
-
- JSPropertyCache* cache = &JS_PROPERTY_CACHE(cx);
- uint32 kshape = OBJ_SHAPE(obj);
+ return true;
+}
+
+bool
+TraceRecorder::record_SetPropHit(uint32 kshape, JSScopeProperty* sprop)
+{
jsbytecode* pc = cx->fp->regs->pc;
-
- 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));
-
+ 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);
+
+ 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");
+
+ 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);
guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);
}
LIns* dslots_ins = NULL;
LIns* v_ins = get(&r);
LIns* boxed_ins = v_ins;
if (!box_jsval(r, boxed_ins))
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()
{
jsval& r = stackval(-1);
jsval& l = stackval(-2);
if (JSVAL_IS_STRING(l) && JSVAL_IS_INT(r)) {
int i;
@@ -5363,18 +5371,18 @@ TraceRecorder::record_JSOP_ENDINIT()
}
}
return true;
}
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
TraceRecorder::record_JSOP_INITELEM()
{
return record_JSOP_SETELEM();
}
@@ -5452,30 +5460,29 @@ TraceRecorder::record_JSOP_ITER()
}
ABORT_TRACE("for-in on a primitive value");
}
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 };
LIns* v_ins = lir->insCall(F_FastCallIteratorNext, args);
guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), OOM_EXIT);
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);
return true;
}
ABORT_TRACE("for-in on a primitive value");
}
@@ -5549,36 +5556,28 @@ TraceRecorder::record_JSOP_BINDNAME()
stack(0, obj_ins);
return true;
}
bool
TraceRecorder::record_JSOP_SETNAME()
{
- jsval& r = stackval(-1);
jsval& l = stackval(-2);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
/*
* Trace cases that are global code or in lightweight functions scoped by
* the global object only.
*/
JSObject* obj = JSVAL_TO_OBJECT(l);
if (obj != cx->fp->scopeChain || obj != globalObj)
- return false;
-
- 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);
+ ABORT_TRACE("JSOP_SETNAME left operand is not the global object");
+
+ // The rest of the work is in record_SetPropHit.
return true;
}
bool
TraceRecorder::record_JSOP_THROW()
{
return false;
}
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -329,17 +329,17 @@ class TraceRecorder {
bool guardShapelessCallee(jsval& callee);
bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc);
bool forInLoop(jsval* vp);
void trackCfgMerges(jsbytecode* pc);
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,
nanojit::GuardRecord* expectedInnerExit);
~TraceRecorder();
uint8 determineSlotType(jsval* vp) const;
nanojit::SideExit* snapshot(nanojit::ExitType exitType);
@@ -353,44 +353,66 @@ public:
bool selectCallablePeerFragment(nanojit::Fragment** first);
void prepareTreeCall(nanojit::Fragment* inner);
void emitTreeCall(nanojit::Fragment* inner, nanojit::GuardRecord* lr);
unsigned getCallDepth() const;
bool trackLoopEdges();
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; }
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
bool record_##op();
# include "jsopcode.tbl"
#undef OPDEF
};
#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);
extern void
js_InitJIT(JSTraceMonitor *tm);
extern void