Bug 495330: trace JSOP_INCNAME and related ops for closures, r=gal
authorDavid Mandelin <dmandelin@mozilla.com>
Fri, 31 Jul 2009 17:52:30 -0700
changeset 31075 e3d2c39d5aa4ef34ae38a18ca504af994c1ccfd0
parent 31074 3fb63f9f32ec1be784a4b7670c29f132985fd32b
child 31076 0c0ad985df66e97164cfc0af4385501858bc1fe0
push id8354
push userrsayre@mozilla.com
push dateMon, 03 Aug 2009 17:31:38 +0000
treeherdermozilla-central@d300a4725056 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs495330
milestone1.9.2a1pre
Bug 495330: trace JSOP_INCNAME and related ops for closures, r=gal
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6750,25 +6750,22 @@ JS_DEFINE_CALLINFO_6(extern, UINT32, Get
 JS_DEFINE_CALLINFO_6(extern, UINT32, GetClosureArg, CONTEXT, OBJECT, UINT32,
                      UINT32, UINT32, DOUBLEPTR, 0, 0)
 
 /*
  * Search the scope chain for a property lookup operation at the current PC and
  * generate LIR to access the given property. Return JSRS_CONTINUE on success,
  * otherwise abort and return JSRS_STOP. There are 3 outparams:
  *
- *     vp         the address of the current property value
- *     ins        LIR instruction representing the property value on trace
- *     tracked    true iff the property value is tracked on this trace. If true,
- *                then the tracked value can be modified using the tracker set
- *                functions. If false, then the value comes from a call to a
- *                builtin to access an upvar, and can't be modified directly.
+ *     vp           the address of the current property value
+ *     ins          LIR instruction representing the property value on trace
+ *     NameResult   describes how to look up name; see comment for NameResult in jstracer.h
  */
 JS_REQUIRES_STACK JSRecordingStatus
-TraceRecorder::scopeChainProp(JSObject* obj, jsval*& vp, LIns*& ins, bool& tracked)
+TraceRecorder::scopeChainProp(JSObject* obj, jsval*& vp, LIns*& ins, NameResult& nr)
 {
     JS_ASSERT(obj != globalObj);
 
     JSAtom* atom = atoms[GET_INDEX(cx->fp->regs->pc)];
     JSObject* obj2;
     JSProperty* prop;
     if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop))
         ABORT_TRACE_ERROR("error in js_FindProperty");
@@ -6788,17 +6785,17 @@ TraceRecorder::scopeChainProp(JSObject* 
         }
         if (!lazilyImportGlobalSlot(sprop->slot)) {
             OBJ_DROP_PROPERTY(cx, obj2, prop);
             ABORT_TRACE("lazy import of global slot failed");
         }
         vp = &STOBJ_GET_SLOT(obj, sprop->slot);
         ins = get(vp);
         OBJ_DROP_PROPERTY(cx, obj2, prop);
-        tracked = true;
+        nr.tracked = true;
         return JSRS_CONTINUE;
     }
 
     if (wasDeepAborted())
         ABORT_TRACE("deep abort from property lookup");
 
     if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
         JSStackFrame* cfp = (JSStackFrame*) JS_GetPrivate(cx, obj);
@@ -6825,17 +6822,17 @@ TraceRecorder::scopeChainProp(JSObject* 
             OBJ_DROP_PROPERTY(cx, obj2, prop);
             if (!vp)
                 ABORT_TRACE("dynamic property of Call object");
 
             if (frameIfInRange(obj)) {
                 // At this point we are guaranteed to be looking at an active call object
                 // whose properties are stored in the corresponding JSStackFrame.
                 ins = get(vp);
-                tracked = true;
+                nr.tracked = true;
                 return JSRS_CONTINUE;
             } else {
                 // Compute number of scope chain links to result.
                 jsint scopeIndex = 0;
                 JSObject* tmp = JSVAL_TO_OBJECT(cx->fp->argv[-2]);
                 while (tmp != obj) {
                     tmp = OBJ_GET_PARENT(cx, tmp);
                     scopeIndex++;
@@ -6860,17 +6857,20 @@ TraceRecorder::scopeChainProp(JSObject* 
 
                 LIns* call_ins = lir->insCall(ci, args);
                 JSTraceType type = getCoercedType(*vp);
                 guard(true,
                       addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)),
                               "guard(type-stable name access)"),
                       BRANCH_EXIT);
                 ins = stackLoad(outp, type);
-                tracked = false;
+                nr.tracked = false;
+                nr.obj = obj;
+                nr.scopeIndex = scopeIndex;
+                nr.sprop = sprop;
                 return JSRS_CONTINUE;
             }
         }
     }
 
     OBJ_DROP_PROPERTY(cx, obj2, prop);
     ABORT_TRACE("fp->scopeChain is not global or active call object");
 }
@@ -9534,23 +9534,30 @@ TraceRecorder::record_JSOP_DECELEM()
     return incElem(-1);
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::incName(jsint incr, bool pre)
 {
     jsval* vp;
     LIns* v_ins;
-    bool tracked;
-    CHECK_STATUS(name(vp, v_ins, tracked));
-    if (!tracked)
-        ABORT_TRACE("incName on non-tracked value not supported");
+    NameResult nr;
+    CHECK_STATUS(name(vp, v_ins, nr));
     CHECK_STATUS(inc(*vp, v_ins, incr, pre));
-    set(vp, v_ins);
-    return JSRS_CONTINUE;
+    if (nr.tracked) {
+        set(vp, v_ins);
+        return JSRS_CONTINUE;
+    }
+
+    if (OBJ_GET_CLASS(cx, nr.obj) != &js_CallClass)
+        ABORT_TRACE("incName on unsupported object class");
+    LIns* callobj_ins = get(&cx->fp->argv[-2]);
+    for (jsint i = 0; i < nr.scopeIndex; ++i)
+        callobj_ins = stobj_get_parent(callobj_ins);
+    return setCallProp(callobj_ins, nr.sprop, v_ins, *vp);
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_NAMEINC()
 {
     return incName(1, false);
 }
 
@@ -9688,36 +9695,18 @@ TraceRecorder::setProp(jsval &l, JSPropC
     JSObject* obj = JSVAL_TO_OBJECT(l);
     LIns* obj_ins = get(&l);
     JSScope* scope = OBJ_SCOPE(obj);
 
     JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->has(sprop));
 
     // Fast path for CallClass. This is about 20% faster than the general case.
     if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
-        const CallInfo* ci = NULL;
-        if (sprop->setter == SetCallArg)
-            ci = &js_SetCallArg_ci;
-        else if (sprop->setter == SetCallVar)
-            ci = &js_SetCallVar_ci;
-        else
-            ABORT_TRACE("can't trace special CallClass setter");
-
         v_ins = get(&v);
-        LIns* v_boxed_ins = v_ins;
-        box_jsval(v, v_boxed_ins);
-        LIns* args[] = {
-            v_boxed_ins,
-            INS_CONST(SPROP_USERID(sprop)),
-            obj_ins,
-            cx_ins
-        };
-        LIns* call_ins = lir->insCall(ci, args);
-        guard(false, addName(lir->ins_eq0(call_ins), "guard(set upvar)"), STATUS_EXIT);
-        return JSRS_CONTINUE;
+        return setCallProp(obj_ins, sprop, v_ins, v);
     }
 
     /*
      * Setting a function-valued property might need to rebrand the object; we
      * don't trace that case. There's no need to guard on that, though, because
      * separating functions into the trace-time type TT_FUNCTION will save the
      * day!
      */
@@ -9753,16 +9742,39 @@ TraceRecorder::setProp(jsval &l, JSPropC
         guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
     }
 
     v_ins = get(&v);
     return nativeSet(obj, obj_ins, sprop, v, v_ins);
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
+TraceRecorder::setCallProp(LIns *callobj_ins, JSScopeProperty *sprop, LIns *v_ins, jsval v)
+{
+    const CallInfo* ci = NULL;
+    if (sprop->setter == SetCallArg)
+        ci = &js_SetCallArg_ci;
+    else if (sprop->setter == SetCallVar)
+        ci = &js_SetCallVar_ci;
+    else
+        ABORT_TRACE("can't trace special CallClass setter");
+
+    box_jsval(v, v_ins);
+    LIns* args[] = {
+        v_ins,
+        INS_CONST(SPROP_USERID(sprop)),
+        callobj_ins,
+        cx_ins
+    };
+    LIns* call_ins = lir->insCall(ci, args);
+    guard(false, addName(lir->ins_eq0(call_ins), "guard(set upvar)"), STATUS_EXIT);
+    return JSRS_CONTINUE;
+}
+
+JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop)
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
     LIns* v_ins;
     CHECK_STATUS(setProp(l, entry, sprop, r, v_ins));
 
     jsbytecode* pc = cx->fp->regs->pc;
@@ -10133,18 +10145,18 @@ TraceRecorder::record_JSOP_SETELEM()
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_CALLNAME()
 {
     JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj) {
         jsval* vp;
         LIns* ins;
-        bool tracked;
-        CHECK_STATUS(scopeChainProp(obj, vp, ins, tracked));
+        NameResult nr;
+        CHECK_STATUS(scopeChainProp(obj, vp, ins, nr));
         stack(0, ins);
         stack(1, INS_CONSTPTR(globalObj));
         return JSRS_CONTINUE;
     }
 
     LIns* obj_ins = scopeChain();
     JSObject* obj2;
     jsuword pcval;
@@ -10601,21 +10613,21 @@ TraceRecorder::record_NativeCallComplete
     }
 
     // We'll null pendingTraceableNative in monitorRecording, on the next op cycle.
     // There must be a next op since the stack is non-empty.
     return ok;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
-TraceRecorder::name(jsval*& vp, LIns*& ins, bool& tracked)
+TraceRecorder::name(jsval*& vp, LIns*& ins, NameResult& nr)
 {
     JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj)
-        return scopeChainProp(obj, vp, ins, tracked);
+        return scopeChainProp(obj, vp, ins, nr);
 
     /* Can't use prop here, because we don't want unboxing from global slots. */
     LIns* obj_ins = scopeChain();
     uint32 slot;
 
     JSObject* obj2;
     jsuword pcval;
 
@@ -10645,17 +10657,17 @@ TraceRecorder::name(jsval*& vp, LIns*& i
         slot = PCVAL_TO_SLOT(pcval);
     }
 
     if (!lazilyImportGlobalSlot(slot))
         ABORT_TRACE("lazy import of global slot failed");
 
     vp = &STOBJ_GET_SLOT(obj, slot);
     ins = get(vp);
-    tracked = true;
+    nr.tracked = true;
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32& slot, LIns*& v_ins)
 {
     /*
      * Can't specialize to assert obj != global, must guard to avoid aliasing
@@ -10915,18 +10927,18 @@ TraceRecorder::getProp(jsval& v)
     return getProp(JSVAL_TO_OBJECT(v), get(&v));
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_NAME()
 {
     jsval* vp;
     LIns* v_ins;
-    bool tracked;
-    CHECK_STATUS(name(vp, v_ins, tracked));
+    NameResult nr;
+    CHECK_STATUS(name(vp, v_ins, nr));
     stack(0, v_ins);
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_DOUBLE()
 {
     jsval v = jsval(atoms[GET_INDEX(cx->fp->regs->pc)]);
@@ -11245,19 +11257,19 @@ TraceRecorder::record_JSOP_ENDITER()
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_FORNAME()
 {
     jsval* vp;
     LIns* x_ins;
-    bool tracked;
-    CHECK_STATUS(name(vp, x_ins, tracked));
-    if (!tracked)
+    NameResult nr;
+    CHECK_STATUS(name(vp, x_ins, nr));
+    if (!nr.tracked)
         ABORT_TRACE("forname on non-tracked value not supported");
     set(vp, stack(-1));
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_FORPROP()
 {
@@ -12218,18 +12230,18 @@ TraceRecorder::record_JSOP_GETXPROP()
         ABORT_TRACE("primitive-this for GETXPROP?");
 
     JSObject* obj = JSVAL_TO_OBJECT(l);
     if (obj != cx->fp->scopeChain || obj != globalObj)
         return JSRS_STOP;
 
     jsval* vp;
     LIns* v_ins;
-    bool tracked;
-    CHECK_STATUS(name(vp, v_ins, tracked));
+    NameResult nr;
+    CHECK_STATUS(name(vp, v_ins, nr));
     stack(-1, v_ins);
     return JSRS_CONTINUE;
 }
 
 JS_REQUIRES_STACK JSRecordingStatus
 TraceRecorder::record_JSOP_CALLXMLNAME()
 {
     return JSRS_STOP;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -622,19 +622,29 @@ class TraceRecorder : public avmplus::GC
     JS_REQUIRES_STACK bool deduceTypeStability(nanojit::Fragment* root_peer,
                                                nanojit::Fragment** stable_peer,
                                                bool& demote);
 
     JS_REQUIRES_STACK jsval& argval(unsigned n) const;
     JS_REQUIRES_STACK jsval& varval(unsigned n) const;
     JS_REQUIRES_STACK jsval& stackval(int n) const;
 
+    struct NameResult {
+        // |tracked| is true iff the result of the name lookup is a variable that
+        // is already in the tracker. The rest of the fields are set only if
+        // |tracked| is false.
+        bool             tracked;
+        JSObject         *obj;           // Call object where name was found
+        jsint            scopeIndex;     // scope chain links from callee to obj
+        JSScopeProperty  *sprop;         // sprop name was resolved to
+    };
+
     JS_REQUIRES_STACK nanojit::LIns* scopeChain() const;
     JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
-    JS_REQUIRES_STACK JSRecordingStatus scopeChainProp(JSObject* obj, jsval*& vp, nanojit::LIns*& ins, bool& tracked);
+    JS_REQUIRES_STACK JSRecordingStatus scopeChainProp(JSObject* obj, jsval*& vp, nanojit::LIns*& ins, NameResult& nr);
 
     JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
     JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i);
     JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
     JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
     JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, jsval& v);
     nanojit::LIns* stackLoad(nanojit::LIns* addr, uint8 type);
     JS_REQUIRES_STACK nanojit::LIns* stack(int n);
@@ -714,32 +724,35 @@ class TraceRecorder : public avmplus::GC
     }
 
     JSRecordingStatus native_get(nanojit::LIns* obj_ins, nanojit::LIns* pobj_ins,
                                  JSScopeProperty* sprop, nanojit::LIns*& dslots_ins,
                                  nanojit::LIns*& v_ins);
 
     nanojit::LIns* getStringLength(nanojit::LIns* str_ins);
 
-    JS_REQUIRES_STACK JSRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, bool& tracked);
+    JS_REQUIRES_STACK JSRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, NameResult& nr);
     JS_REQUIRES_STACK JSRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, uint32& slot,
                                              nanojit::LIns*& v_ins);
     JS_REQUIRES_STACK JSRecordingStatus denseArrayElement(jsval& oval, jsval& idx, jsval*& vp,
                                                           nanojit::LIns*& v_ins,
                                                           nanojit::LIns*& addr_ins);
     JS_REQUIRES_STACK JSRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins);
     JS_REQUIRES_STACK JSRecordingStatus getProp(jsval& v);
     JS_REQUIRES_STACK JSRecordingStatus getThis(nanojit::LIns*& this_ins);
 
     JS_REQUIRES_STACK JSRecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
                                                   JSScopeProperty* sprop,
                                                   jsval v, nanojit::LIns* v_ins);
     JS_REQUIRES_STACK JSRecordingStatus setProp(jsval &l, JSPropCacheEntry* entry,
                                                 JSScopeProperty* sprop,
                                                 jsval &v, nanojit::LIns*& v_ins);
+    JS_REQUIRES_STACK JSRecordingStatus setCallProp(nanojit::LIns *callobj_ins,
+                                                    JSScopeProperty *sprop, nanojit::LIns *v_ins,
+                                                    jsval v);
 
     JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins);
     JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins, VMSideExit* exit);
     JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
                                       VMSideExit* exit);
     JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
                                            ExitType exitType = MISMATCH_EXIT);
     JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins,