Move GETELEM/SETELEM operations to jsinterpinlines (bug 718683, r=pierron)
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 24 Jan 2012 16:20:34 +0100
changeset 86460 dc059f35f32bfa25aa8f699f328926b0078721ca
parent 86459 da20a9c178f58a1a951f9f6c98ca85a93c780288
child 86461 2a9c391e2a8985057bb68b53a281ace0b46d67fc
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspierron
bugs718683
milestone12.0a1
Move GETELEM/SETELEM operations to jsinterpinlines (bug 718683, r=pierron)
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1103,16 +1103,17 @@ class TypeScript
      * Monitor a bytecode pushing any value. This must be called for any opcode
      * which is JOF_TYPESET, and where either the script has not been analyzed
      * by type inference or where the pc has type barriers. For simplicity, we
      * always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
      * and only look at barriers when generating JIT code for the script.
      */
     static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc,
                                const js::Value &val);
+    static inline void Monitor(JSContext *cx, const js::Value &rval);
 
     /* Monitor an assignment at a SETELEM on a non-integer identifier. */
     static inline void MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc,
                                      JSObject *obj, jsid id, const js::Value &val);
 
     /* Add a type for a variable in a script. */
     static inline void SetThis(JSContext *cx, JSScript *script, Type type);
     static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -625,16 +625,25 @@ TypeScript::MonitorUnknown(JSContext *cx
 {
     JSScript *script;
     jsbytecode *pc;
     GetPcScript(cx, &script, &pc);
     MonitorUnknown(cx, script, pc);
 }
 
 /* static */ inline void
+TypeScript::Monitor(JSContext *cx, const js::Value &rval)
+{
+    JSScript *script;
+    jsbytecode *pc;
+    GetPcScript(cx, &script, &pc);
+    Monitor(cx, script, pc, rval);
+}
+
+/* static */ inline void
 TypeScript::MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc,
                           JSObject *obj, jsid id, const js::Value &rval)
 {
     if (cx->typeInferenceEnabled() && !obj->hasSingletonType()) {
         /*
          * Mark as unknown any object which has had dynamic assignments to
          * non-integer properties at SETELEM opcodes. This avoids making large
          * numbers of type properties for hashmap-style objects. We don't need
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2716,88 +2716,18 @@ BEGIN_CASE(JSOP_SETMETHOD)
     regs.sp--;
 }
 END_CASE(JSOP_SETPROP)
 
 BEGIN_CASE(JSOP_GETELEM)
 {
     Value &lref = regs.sp[-2];
     Value &rref = regs.sp[-1];
-    Value &rval = regs.sp[-2];
-    if (lref.isString() && rref.isInt32()) {
-        JSString *str = lref.toString();
-        int32_t i = rref.toInt32();
-        if (size_t(i) < str->length()) {
-            str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
-            if (!str)
-                goto error;
-            rval.setString(str);
-            TypeScript::Monitor(cx, script, regs.pc, rval);
-            regs.sp--;
-            len = JSOP_GETELEM_LENGTH;
-            DO_NEXT_OP(len);
-        }
-    }
-
-    if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
-        if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
-            rval = regs.fp()->canonicalActualArg(rref.toInt32());
-            TypeScript::Monitor(cx, script, regs.pc, rval);
-            regs.sp--;
-            len = JSOP_GETELEM_LENGTH;
-            DO_NEXT_OP(len);
-        }
-        MarkArgumentsCreated(cx, script);
-        JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
-    }
-
-    JSObject *obj;
-    VALUE_TO_OBJECT(cx, &lref, obj);
-
-    uint32_t index;
-    if (IsDefinitelyIndex(rref, &index)) {
-        if (obj->isDenseArray()) {
-            if (index < obj->getDenseArrayInitializedLength()) {
-                rval = obj->getDenseArrayElement(index);
-                if (!rval.isMagic())
-                    goto end_getelem;
-            }
-        } else if (obj->isArguments()) {
-            if (obj->asArguments().getElement(index, &rval))
-                goto end_getelem;
-        }
-
-        if (!obj->getElement(cx, index, &rval))
-            goto error;
-    } else {
-        if (script->hasAnalysis())
-            script->analysis()->getCode(regs.pc).getStringElement = true;
-
-        SpecialId special;
-        if (ValueIsSpecial(obj, &rref, &special, cx)) {
-            if (!obj->getSpecial(cx, obj, special, &rval))
-                goto error;
-        } else {
-            JSAtom *name;
-            if (!js_ValueToAtom(cx, rref, &name))
-                goto error;
-
-            if (name->isIndex(&index)) {
-                if (!obj->getElement(cx, index, &rval))
-                    goto error;
-            } else {
-                if (!obj->getProperty(cx, name->asPropertyName(), &rval))
-                    goto error;
-            }
-        }
-    }
-
-  end_getelem:
-    assertSameCompartment(cx, rval);
-    TypeScript::Monitor(cx, script, regs.pc, rval);
+    if (!GetElementOperation(cx, lref, rref, &regs.sp[-2]))
+        goto error;
     regs.sp--;
 }
 END_CASE(JSOP_GETELEM)
 
 BEGIN_CASE(JSOP_CALLELEM)
 {
     /* Find the object on which to look for |this|'s properties. */
     Value thisv = regs.sp[-2];
@@ -2827,42 +2757,20 @@ BEGIN_CASE(JSOP_CALLELEM)
 END_CASE(JSOP_CALLELEM)
 
 BEGIN_CASE(JSOP_SETELEM)
 {
     JSObject *obj;
     FETCH_OBJECT(cx, -3, obj);
     jsid id;
     FETCH_ELEMENT_ID(obj, -2, id);
-    Value rval;
-    TypeScript::MonitorAssign(cx, script, regs.pc, obj, id, regs.sp[-1]);
-    do {
-        if (obj->isDenseArray() && JSID_IS_INT(id)) {
-            jsuint length = obj->getDenseArrayInitializedLength();
-            jsint i = JSID_TO_INT(id);
-            if ((jsuint)i < length) {
-                if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
-                    if (js_PrototypeHasIndexedProperties(cx, obj))
-                        break;
-                    if ((jsuint)i >= obj->getArrayLength())
-                        obj->setArrayLength(cx, i + 1);
-                }
-                obj->setDenseArrayElementWithType(cx, i, regs.sp[-1]);
-                goto end_setelem;
-            } else {
-                if (script->hasAnalysis())
-                    script->analysis()->getCode(regs.pc).arrayWriteHole = true;
-            }
-        }
-    } while (0);
-    rval = regs.sp[-1];
-    if (!obj->setGeneric(cx, id, &rval, script->strictModeCode))
+    Value &value = regs.sp[-1];
+    if (!SetObjectElementOperation(cx, obj, id, value))
         goto error;
-  end_setelem:
-    regs.sp[-3] = regs.sp[-1];
+    regs.sp[-3] = value;
     regs.sp -= 2;
 }
 END_CASE(JSOP_SETELEM)
 
 BEGIN_CASE(JSOP_ENUMELEM)
 {
     /* Funky: the value to set is under the [obj, id] pair. */
     JSObject *obj;
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -640,11 +640,138 @@ ModOperation(JSContext *cx, const Value 
     if (d2 == 0)
         res->setDouble(js_NaN);
     else
         res->setDouble(js_fmod(d1, d2));
     types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
+static inline bool
+FetchElementId(JSContext *cx, JSObject *obj, const Value &idval, jsid &id, Value *vp)
+{
+    int32_t i_;
+    if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
+        id = INT_TO_JSID(i_);
+        return true;
+    }
+    return !!js_InternNonIntElementId(cx, obj, idval, &id, vp);
+}
+
+static JS_ALWAYS_INLINE bool
+GetObjectElementOperation(JSContext *cx, JSObject *obj, const Value &rref, Value *res)
+{
+    JSScript *script;
+    jsbytecode *pc;
+    types::TypeScript::GetPcScript(cx, &script, &pc);
+
+    uint32_t index;
+    if (IsDefinitelyIndex(rref, &index)) {
+        do {
+            if (obj->isDenseArray()) {
+                if (index < obj->getDenseArrayInitializedLength()) {
+                    *res = obj->getDenseArrayElement(index);
+                    if (!res->isMagic())
+                        break;
+                }
+            } else if (obj->isArguments()) {
+                if (obj->asArguments().getElement(index, res))
+                    break;
+            }
+            if (!obj->getElement(cx, index, res))
+                return false;
+        } while(0);
+    } else {
+        if (script->hasAnalysis())
+            script->analysis()->getCode(pc).getStringElement = true;
+
+        SpecialId special;
+        *res = rref;
+        if (ValueIsSpecial(obj, res, &special, cx)) {
+            if (!obj->getSpecial(cx, obj, special, res))
+                return false;
+        } else {
+            JSAtom *name;
+            if (!js_ValueToAtom(cx, *res, &name))
+                return false;
+
+            if (name->isIndex(&index)) {
+                if (!obj->getElement(cx, index, res))
+                    return false;
+            } else {
+                if (!obj->getProperty(cx, name->asPropertyName(), res))
+                    return false;
+            }
+        }
+    }
+
+    assertSameCompartment(cx, *res);
+    types::TypeScript::Monitor(cx, script, pc, *res);
+    return true;
+}
+
+static JS_ALWAYS_INLINE bool
+GetElementOperation(JSContext *cx, const Value &lref, const Value &rref, Value *res)
+{
+    if (lref.isString() && rref.isInt32()) {
+        JSString *str = lref.toString();
+        int32_t i = rref.toInt32();
+        if (size_t(i) < str->length()) {
+            str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
+            if (!str)
+                return false;
+            res->setString(str);
+            types::TypeScript::Monitor(cx, *res);
+            return true;
+        }
+    }
+
+    if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
+        if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) {
+            *res = cx->regs().fp()->canonicalActualArg(rref.toInt32());
+            types::TypeScript::Monitor(cx, *res);
+            return true;
+        }
+        types::MarkArgumentsCreated(cx, cx->fp()->script());
+        JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
+    }
+
+    JSObject *obj = ValueToObject(cx, lref);
+    if (!obj)
+        return false;
+    return GetObjectElementOperation(cx, obj, rref, res);
+}
+
+static JS_ALWAYS_INLINE bool
+SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value)
+{
+    JSScript *script;
+    jsbytecode *pc;
+    types::TypeScript::GetPcScript(cx, &script, &pc);
+    types::TypeScript::MonitorAssign(cx, script, pc, obj, id, value);
+
+    do {
+        if (obj->isDenseArray() && JSID_IS_INT(id)) {
+            jsuint length = obj->getDenseArrayInitializedLength();
+            jsint i = JSID_TO_INT(id);
+            if ((jsuint)i < length) {
+                if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
+                    if (js_PrototypeHasIndexedProperties(cx, obj))
+                        break;
+                    if ((jsuint)i >= obj->getArrayLength())
+                        obj->setArrayLength(cx, i + 1);
+                }
+                obj->setDenseArrayElementWithType(cx, i, value);
+                return true;
+            } else {
+                if (script->hasAnalysis())
+                    script->analysis()->getCode(pc).arrayWriteHole = true;
+            }
+        }
+    } while (0);
+
+    Value tmp = value;
+    return obj->setGeneric(cx, id, &tmp, script->strictModeCode);
+}
+
 }  /* namespace js */
 
 #endif /* jsinterpinlines_h__ */
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -205,27 +205,16 @@ stubs::GetElem(VMFrame &f)
 #if JS_HAS_NO_SUCH_METHOD
     if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(rval.isPrimitive()) && isObject) {
         if (!OnUnknownMethod(cx, obj, rref, &rval))
             THROW();
     }
 #endif
 }
 
-static inline bool
-FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)
-{
-    int32_t i_;
-    if (ValueFitsInInt32(idval, &i_) && INT_FITS_IN_JSID(i_)) {
-        id = INT_TO_JSID(i_);
-        return true;
-    }
-    return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
-}
-
 template<JSBool strict>
 void JS_FASTCALL
 stubs::SetElem(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     Value &objval = regs.sp[-3];
@@ -234,17 +223,17 @@ stubs::SetElem(VMFrame &f)
 
     JSObject *obj;
     jsid id;
 
     obj = ValueToObject(cx, objval);
     if (!obj)
         THROW();
 
-    if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
+    if (!FetchElementId(f.cx, obj, idval, id, &regs.sp[-2]))
         THROW();
 
     TypeScript::MonitorAssign(cx, f.script(), f.pc(), obj, id, rval);
 
     do {
         if (obj->isDenseArray() && JSID_IS_INT(id)) {
             jsuint length = obj->getDenseArrayInitializedLength();
             jsint i = JSID_TO_INT(id);
@@ -282,17 +271,17 @@ stubs::ToId(VMFrame &f)
     Value &objval = f.regs.sp[-2];
     Value &idval  = f.regs.sp[-1];
 
     JSObject *obj = ValueToObject(f.cx, objval);
     if (!obj)
         THROW();
 
     jsid id;
-    if (!FetchElementId(f, obj, idval, id, &idval))
+    if (!FetchElementId(f.cx, obj, idval, id, &idval))
         THROW();
 
     if (!idval.isInt32())
         TypeScript::MonitorUnknown(f.cx, f.script(), f.pc());
 }
 
 void JS_FASTCALL
 stubs::ImplicitThis(VMFrame &f, PropertyName *name)
@@ -1027,17 +1016,17 @@ stubs::InitElem(VMFrame &f, uint32_t las
     /* Find the object being initialized at top of stack. */
     const Value &lref = regs.sp[-3];
     JS_ASSERT(lref.isObject());
     JSObject *obj = &lref.toObject();
 
     /* Fetch id now that we have obj. */
     jsid id;
     const Value &idval = regs.sp[-2];
-    if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
+    if (!FetchElementId(f.cx, obj, idval, id, &regs.sp[-2]))
         THROW();
 
     /*
      * If rref is a hole, do not call JSObject::defineProperty. In this case,
      * obj must be an array, so if the current op is the last element
      * initialiser, set the array length to one greater than id.
      */
     if (rref.isMagic(JS_ARRAY_HOLE)) {
@@ -1739,17 +1728,17 @@ stubs::In(VMFrame &f)
     const Value &rref = f.regs.sp[-1];
     if (!rref.isObject()) {
         js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NULL);
         THROWV(JS_FALSE);
     }
 
     JSObject *obj = &rref.toObject();
     jsid id;
-    if (!FetchElementId(f, obj, f.regs.sp[-2], id, &f.regs.sp[-2]))
+    if (!FetchElementId(f.cx, obj, f.regs.sp[-2], id, &f.regs.sp[-2]))
         THROWV(JS_FALSE);
 
     JSObject *obj2;
     JSProperty *prop;
     if (!obj->lookupGeneric(cx, id, &obj2, &prop))
         THROWV(JS_FALSE);
 
     return !!prop;