[INFER] Speculate that all ELEM accesses are on integers, use monitoring for strings, bug 642412.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 18 Mar 2011 10:30:21 -0700
changeset 74809 b48f1d51c6f59dee7a0540b654f17b1106ae25e6
parent 74808 ac5f63354ab2d2f43f49dc96b5d675b29a02d4fd
child 74810 f1dff744b6c8f04c4f750a547209eac9c37da2b5
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs642412
milestone2.0b13pre
[INFER] Speculate that all ELEM accesses are on integers, use monitoring for strings, bug 642412.
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinterp.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -606,57 +606,16 @@ TypeSet::addGetProperty(JSContext *cx, J
 
 void
 TypeSet::addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id)
 {
     add(cx, ArenaNew<TypeConstraintProp>(cx->compartment->types.pool, script, pc, target, id, true));
 }
 
-/*
- * Constraints for reads/writes on object elements, which may either be integer
- * element accesses or arbitrary accesses depending on the index type.
- */
-class TypeConstraintElem : public TypeConstraint
-{
-public:
-    const jsbytecode *pc;
-
-    /* Types of the object being accessed. */
-    TypeSet *object;
-
-    /* Target to use for the ConstraintProp. */
-    TypeSet *target;
-
-    /* As for ConstraintProp. */
-    bool assign;
-
-    TypeConstraintElem(JSScript *script, const jsbytecode *pc,
-                       TypeSet *object, TypeSet *target, bool assign)
-        : TypeConstraint("elem", script), pc(pc),
-          object(object), target(target), assign(assign)
-    {}
-
-    void newType(JSContext *cx, TypeSet *source, jstype type);
-};
-
-void
-TypeSet::addGetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
-                    TypeSet *object, TypeSet *target)
-{
-    add(cx, ArenaNew<TypeConstraintElem>(cx->compartment->types.pool, script, pc, object, target, false));
-}
-
-void
-TypeSet::addSetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
-                    TypeSet *object, TypeSet *target)
-{
-    add(cx, ArenaNew<TypeConstraintElem>(cx->compartment->types.pool, script, pc, object, target, true));
-}
-
 /* Constraints for determining the 'this' object at sites invoked using 'new'. */
 class TypeConstraintNewObject : public TypeConstraint
 {
     TypeFunction *fun;
     TypeSet *target;
 
   public:
     TypeConstraintNewObject(JSScript *script, TypeFunction *fun, TypeSet *target)
@@ -937,48 +896,16 @@ TypeConstraintProp::newType(JSContext *c
     }
 
     TypeObject *object = GetPropertyObject(cx, script, type);
     if (object)
         PropertyAccess(cx, script, pc, object, assign, target, id);
 }
 
 void
-TypeConstraintElem::newType(JSContext *cx, TypeSet *source, jstype type)
-{
-    switch (type) {
-      case TYPE_UNDEFINED:
-      case TYPE_BOOLEAN:
-      case TYPE_NULL:
-      case TYPE_INT32:
-      case TYPE_DOUBLE:
-        /*
-         * Integer index access, these are all covered by the JSID_VOID property.
-         * We are optimistically treating non-number accesses as not actually occurring,
-         * and double accesses as getting an integer property. This must be checked
-         * at runtime.
-         */
-        if (assign)
-            object->addSetProperty(cx, script, pc, target, JSID_VOID);
-        else
-            object->addGetProperty(cx, script, pc, target, JSID_VOID);
-        break;
-      default:
-        /*
-         * Access to a potentially arbitrary element. Monitor assignments to unknown
-         * elements, and treat reads of unknown elements as unknown.
-         */
-        if (assign)
-            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
-        else
-            target->addType(cx, TYPE_UNKNOWN);
-    }
-}
-
-void
 TypeConstraintNewObject::newType(JSContext *cx, TypeSet *source, jstype type)
 {
     if (type == TYPE_UNKNOWN) {
         target->addType(cx, TYPE_UNKNOWN);
         return;
     }
 
     if (TypeIsObject(type)) {
@@ -3163,36 +3090,39 @@ AnalyzeBytecode(JSContext *cx, AnalyzeSt
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
       }
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM:
-        state.popped(0).types->addGetElem(cx, script, pc, state.popped(1).types, &pushed[0]);
+        /*
+         * We only consider ELEM accesses on integers here. Any element access
+         * which is accessing a non-integer property must be monitored.
+         */
+        state.popped(1).types->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
 
         if (op == JSOP_CALLELEM)
             state.popped(1).types->addFilterPrimitives(cx, script, &pushed[1], true);
         if (CheckNextTest(pc))
             pushed[0].addType(cx, TYPE_UNDEFINED);
         break;
 
       case JSOP_SETELEM:
-        state.popped(1).types->addSetElem(cx, script, pc, state.popped(2).types,
-                                          state.popped(0).types);
+        state.popped(2).types->addSetProperty(cx, script, pc, state.popped(0).types, JSID_VOID);
         state.popped(0).types->addSubset(cx, script, &pushed[0]);
         break;
 
       case JSOP_INCELEM:
       case JSOP_DECELEM:
       case JSOP_ELEMINC:
       case JSOP_ELEMDEC:
-        state.popped(0).types->addGetElem(cx, script, pc, state.popped(1).types, &pushed[0]);
-        state.popped(0).types->addSetElem(cx, script, pc, state.popped(1).types, NULL);
+        state.popped(1).types->addGetProperty(cx, script, pc, &pushed[0], JSID_VOID);
+        state.popped(1).types->addSetProperty(cx, script, pc, NULL, JSID_VOID);
         break;
 
       case JSOP_LENGTH:
         /* Treat this as an access to the length property. */
         state.popped(0).types->addGetProperty(cx, script, pc, &pushed[0], id_length(cx));
         break;
 
       case JSOP_THIS:
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -276,20 +276,16 @@ struct TypeSet
 
     /* Add specific kinds of constraints to this set. */
     inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
     void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
     void addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id);
     void addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
                         TypeSet *target, jsid id);
-    void addGetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
-                    TypeSet *object, TypeSet *target);
-    void addSetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
-                    TypeSet *object, TypeSet *target);
     void addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target);
     void addCall(JSContext *cx, TypeCallsite *site);
     void addArith(JSContext *cx, JSScript *script,
                   TypeSet *target, TypeSet *other = NULL);
     void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
     void addFilterPrimitives(JSContext *cx, JSScript *script,
                              TypeSet *target, bool onlyNullVoid);
     void addMonitorRead(JSContext *cx, JSScript *script, TypeSet *target);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4052,18 +4052,21 @@ BEGIN_CASE(JSOP_DECPROP)
 BEGIN_CASE(JSOP_PROPINC)
 BEGIN_CASE(JSOP_PROPDEC)
     LOAD_ATOM(0, atom);
     id = ATOM_TO_JSID(atom);
     i = -1;
 
   fetch_incop_obj:
     FETCH_OBJECT(cx, i, obj);
-    if (JSID_IS_VOID(id))
+    if (JSID_IS_VOID(id)) {
         FETCH_ELEMENT_ID(obj, -1, id);
+        if (!JSID_IS_INT(id) && !script->typeMonitorUnknown(cx, regs.pc))
+            goto error;
+    }
     goto do_incop;
 
 BEGIN_CASE(JSOP_INCNAME)
 BEGIN_CASE(JSOP_DECNAME)
 BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 BEGIN_CASE(JSOP_INCGNAME)
 BEGIN_CASE(JSOP_DECGNAME)
@@ -4682,24 +4685,24 @@ BEGIN_CASE(JSOP_GETELEM)
                 goto error;
         }
     }
 
     if (!obj->getProperty(cx, id, &rval))
         goto error;
     copyFrom = &rval;
 
+    if (!JSID_IS_INT(id) && !script->typeMonitorUnknown(cx, regs.pc))
+        goto error;
+
   end_getelem:
     regs.sp--;
     regs.sp[-1] = *copyFrom;
     assertSameCompartment(cx, regs.sp[-1]);
-    if (!rref.isInt32()) {
-        if (!script->typeMonitorUnknown(cx, regs.pc))
-            goto error;
-    } else if (copyFrom->isUndefined()) {
+    if (copyFrom->isUndefined()) {
         cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
         if (!script->typeMonitorUndefined(cx, regs.pc))
             goto error;
     }
 }
 END_CASE(JSOP_GETELEM)
 
 BEGIN_CASE(JSOP_CALLELEM)
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -2438,16 +2438,18 @@ ic::CallElement(VMFrame &f, ic::GetEleme
         LookupStatus status = ic->update(cx, thisObj, idval, id, &f.regs.sp[-2]);
         if (status != Lookup_Uncacheable) {
             if (status == Lookup_Error)
                 THROW();
 
             // If the result can be cached, the value was already retrieved.
             JS_ASSERT(!f.regs.sp[-2].isMagic());
             f.regs.sp[-1].setObject(*thisObj);
+            if (!JSID_IS_INT(id) && !f.script()->typeMonitorUnknown(cx, f.regs.pc))
+                THROW();
             return;
         }
     }
 
     /* Get or set the element. */
     if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &f.regs.sp[-2]))
         THROW();
 
@@ -2457,16 +2459,18 @@ ic::CallElement(VMFrame &f, ic::GetEleme
         f.regs.sp[-1].setObject(*thisObj);
         if (!js_OnUnknownMethod(cx, f.regs.sp - 2))
             THROW();
     } else
 #endif
     {
         f.regs.sp[-1] = thisv;
     }
+    if (!JSID_IS_INT(id) && !f.script()->typeMonitorUnknown(cx, f.regs.pc))
+        THROW();
     if (f.regs.sp[-2].isUndefined() && !f.script()->typeMonitorUndefined(cx, f.regs.pc))
         THROW();
 }
 
 void JS_FASTCALL
 ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
 {
     JSContext *cx = f.cx;
@@ -2498,22 +2502,26 @@ ic::GetElement(VMFrame &f, ic::GetElemen
 #endif
         LookupStatus status = ic->update(cx, obj, idval, id, &f.regs.sp[-2]);
         if (status != Lookup_Uncacheable) {
             if (status == Lookup_Error)
                 THROW();
 
             // If the result can be cached, the value was already retrieved.
             JS_ASSERT(!f.regs.sp[-2].isMagic());
+            if (!JSID_IS_INT(id) && !f.script()->typeMonitorUnknown(cx, f.regs.pc))
+                THROW();
             return;
         }
     }
 
     if (!obj->getProperty(cx, id, &f.regs.sp[-2]))
         THROW();
+    if (!JSID_IS_INT(id) && !f.script()->typeMonitorUnknown(cx, f.regs.pc))
+        THROW();
     if (f.regs.sp[-2].isUndefined()) {
         if (idval.isInt32())
             cx->addTypeProperty(obj->getType(), NULL, types::TYPE_UNDEFINED);
         if (!f.script()->typeMonitorUndefined(cx, f.regs.pc))
             THROW();
     }
 }
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -486,23 +486,23 @@ stubs::GetElem(VMFrame &f)
                 THROW();
         }
     }
 
     if (!obj->getProperty(cx, id, &rval))
         THROW();
     copyFrom = &rval;
 
+    if (!JSID_IS_INT(id) && !f.script()->typeMonitorUnknown(cx, regs.pc))
+        THROW();
+
   end_getelem:
     f.regs.sp[-2] = *copyFrom;
 
-    if (!rref.isInt32()) {
-        if (!f.script()->typeMonitorUnknown(cx, regs.pc))
-            THROW();
-    } else if (copyFrom->isUndefined()) {
+    if (copyFrom->isUndefined()) {
         cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
         if (!f.script()->typeMonitorUndefined(cx, regs.pc))
             THROW();
     }
 }
 
 static inline bool
 FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *vp)