Bug 818050. Teach the JIT about DOM method and getter return types so that it doesn't have to type-guard when unboxing the boxed value in many cases. r=jandem,peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 03 Jan 2013 14:02:52 -0500
changeset 123046 150d2a82c060025ff013b7e71118f9637f624262
parent 123045 c80717e72675f0f8698e3d58e682fa5a6425d341
child 123047 cc603feaa2862586bfc2c5eefdfdadf70401d09b
push idunknown
push userunknown
push dateunknown
reviewersjandem, peterv
bugs818050
milestone20.0a1
Bug 818050. Teach the JIT about DOM method and getter return types so that it doesn't have to type-guard when unboxing the boxed value in many cases. r=jandem,peterv
dom/bindings/Codegen.py
js/src/ion/IonBuilder.cpp
js/src/jsfriendapi.h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4541,47 +4541,53 @@ class CGMemberJITInfo(CGThing):
     """
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
-    def defineJitInfo(self, infoName, opName, opType, infallible, constant):
+    def defineJitInfo(self, infoName, opName, opType, infallible, constant,
+                      returnTypes):
         protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = toStringBool(infallible)
         conststr = toStringBool(constant)
+        returnType = reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
+                            "")
         return ("\n"
                 "const JSJitInfo %s = {\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  JSJitInfo::%s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
-                "  %s  /* isConstant. Only relevant for getters. */\n"
+                "  %s,  /* isConstant. Only relevant for getters. */\n"
+                "  %s   /* returnType.  Only relevant for getters/methods. */\n"
                 "};\n" % (infoName, opName, protoID, depth, opType, failstr,
-                          conststr))
+                          conststr, returnType))
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             getterconst = self.member.getExtendedAttribute("Constant")
             result = self.defineJitInfo(getterinfo, getter, "Getter",
-                                        getterinfal, getterconst)
+                                        getterinfal, getterconst,
+                                        [self.member.type])
             if not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None:
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, "Setter",
-                                             False, False)
+                                             False, False,
+                                             [BuiltinTypes[IDLBuiltinType.Types.void]])
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             name = CppKeywords.checkMethodName(self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
             method = ("(JSJitPropertyOp)%s" % name)
 
             # Methods are infallible if they are infallible, have no arguments
@@ -4593,20 +4599,95 @@ class CGMemberJITInfo(CGThing):
                 # Don't handle overloading. If there's more than one signature,
                 # one of them must take arguments.
                 sig = sigs[0]
                 if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
                     # No arguments and infallible return boxing
                     methodInfal = True
 
             result = self.defineJitInfo(methodinfo, method, "Method",
-                                        methodInfal, False)
+                                        methodInfal, False,
+                                        [s[0] for s in sigs])
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
+    @staticmethod
+    def getJSReturnTypeTag(t):
+        if t.nullable():
+            # Sometimes it might return null, sometimes not
+            return "JSVAL_TYPE_UNKNOWN"
+        if t.isVoid():
+            # No return, every time
+            return "JSVAL_TYPE_UNDEFINED"
+        if t.isArray():
+            # No idea yet
+            assert False
+        if t.isSequence():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isGeckoInterface():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isString():
+            return "JSVAL_TYPE_STRING"
+        if t.isEnum():
+            return "JSVAL_TYPE_STRING"
+        if t.isCallback():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isAny():
+            # The whole point is to return various stuff
+            return "JSVAL_TYPE_UNKNOWN"
+        if t.isObject():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isSpiderMonkeyInterface():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isUnion():
+            u = t.unroll();
+            if u.hasNullableType:
+                # Might be null or not
+                return "JSVAL_TYPE_UNKNOWN"
+            return reduce(CGMemberJITInfo.getSingleReturnType,
+                          u.flatMemberTypes, "")
+        if t.isDictionary():
+            return "JSVAL_TYPE_OBJECT"
+        if not t.isPrimitive():
+            raise TypeError("No idea what type " + str(t) + " is.")
+        tag = t.tag()
+        if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+                   IDLType.Tags.int16, IDLType.Tags.uint16,
+                   IDLType.Tags.int32, IDLType.Tags.bool]:
+            return "JSVAL_TYPE_INT32"
+        if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
+                   IDLType.Tags.unrestricted_float, IDLType.Tags.float,
+                   IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
+            # These all use JS_NumberValue, which can return int or double.
+            # But TI treats "double" as meaning "int or double", so we're
+            # good to return JSVAL_TYPE_DOUBLE here.
+            return "JSVAL_TYPE_DOUBLE"
+        if tag != IDLType.Tags.uint32:
+            raise TypeError("No idea what type " + str(t) + " is.")
+        # uint32 is sometimes int and sometimes double.
+        return "JSVAL_TYPE_DOUBLE"
+
+    @staticmethod
+    def getSingleReturnType(existingType, t):
+        type = CGMemberJITInfo.getJSReturnTypeTag(t)
+        if existingType == "":
+            # First element of the list; just return its type
+            return type
+
+        if type == existingType:
+            return existingType
+        if ((type == "JSVAL_TYPE_DOUBLE" and
+             existingType == "JSVAL_TYPE_INT32") or
+            (existingType == "JSVAL_TYPE_DOUBLE" and
+             type == "JSVAL_TYPE_INT32")):
+            # Promote INT32 to DOUBLE as needed
+            return "JSVAL_TYPE_DOUBLE"
+        # Different types
+        return "JSVAL_TYPE_UNKNOWN"
+
 def getEnumValueName(value):
     # Some enum values can be empty strings.  Others might have weird
     # characters in them.  Deal with the former by returning "_empty",
     # deal with possible name collisions from that by throwing if the
     # enum value is actually "_empty", and throw on any value
     # containing non-ASCII chars for now. Replace all chars other than
     # [0-9A-Za-z_] with '_'.
     if re.match("[^\x20-\x7E]", value):
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4085,16 +4085,37 @@ IonBuilder::makeCallHelper(HandleFunctio
         }
     }
     call->initFunction(fun);
 
     current->add(call);
     return call;
 }
 
+static types::StackTypeSet*
+AdjustTypeBarrierForDOMCall(const JSJitInfo* jitinfo, types::StackTypeSet *types,
+                            types::StackTypeSet *barrier)
+{
+    // If the return type of our DOM native is in "types" already, we don't
+    // actually need a barrier.
+    if (jitinfo->returnType == JSVAL_TYPE_UNKNOWN)
+        return barrier;
+
+    // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
+    // actual type of the object.
+    if (jitinfo->returnType == JSVAL_TYPE_OBJECT)
+        return barrier;
+
+    if (jitinfo->returnType != types->getKnownTypeTag())
+        return barrier;
+    
+    // No need for a barrier if we're already expecting the type we'll produce.
+    return NULL;
+}
+
 bool
 IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc,
                             bool constructing,
                             types::StackTypeSet *types,
                             types::StackTypeSet *barrier)
 {
     Vector<MPassArg *> args(cx);
     MPassArg *thisArg;
@@ -4114,16 +4135,22 @@ IonBuilder::makeCallBarrier(HandleFuncti
     MCall *call = makeCallHelper(target, constructing, fun, thisArg, args);
     if (!call)
         return false;
 
     current->push(call);
     if (!resumeAfter(call))
         return false;
 
+    if (call->isDOMFunction()) {
+        JSFunction* target = call->getSingleTarget();
+        JS_ASSERT(target && target->isNative() && target->jitInfo());
+        barrier = AdjustTypeBarrierForDOMCall(target->jitInfo(), types, barrier);
+    }
+
     return pushTypeBarrier(call, types, barrier);
 }
 
 bool
 IonBuilder::makeCall(HandleFunction target, uint32_t argc, bool constructing)
 {
     Vector<MPassArg *> args(cx);
     MPassArg *thisArg;
@@ -6230,16 +6257,17 @@ IonBuilder::getPropTryCommonGetter(bool 
     if (isDOM && TestShouldDOMCall(cx, unaryTypes.inTypes, getter, JSJitInfo::Getter)) {
         const JSJitInfo *jitinfo = getter->jitInfo();
         MGetDOMProperty *get = MGetDOMProperty::New(jitinfo, obj, guard);
         current->add(get);
         current->push(get);
 
         if (get->isEffectful() && !resumeAfter(get))
             return false;
+        barrier = AdjustTypeBarrierForDOMCall(jitinfo, types, barrier);
         if (!pushTypeBarrier(get, types, barrier))
             return false;
 
         *emitted = true;
         return true;
     }
 
     // Spoof stack to expected state for call.
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1442,18 +1442,19 @@ struct JSJitInfo {
         Setter,
         Method
     };
 
     JSJitPropertyOp op;
     uint32_t protoID;
     uint32_t depth;
     OpType type;
-    bool isInfallible;    /* Is op fallible? False in setters. */
-    bool isConstant;      /* Getting a construction-time constant? */
+    bool isInfallible;      /* Is op fallible? False in setters. */
+    bool isConstant;        /* Getting a construction-time constant? */
+    JSValueType returnType; /* The return type tag.  Might be JSVAL_TYPE_UNKNOWN */
 };
 
 static JS_ALWAYS_INLINE const JSJitInfo *
 FUNCTION_VALUE_TO_JITINFO(const JS::Value& v)
 {
     JS_ASSERT(js::GetObjectClass(&v.toObject()) == &js::FunctionClass);
     return reinterpret_cast<js::shadow::Function *>(&v.toObject())->jitinfo;
 }