Bug 939835. Fix up performance regressions from bug 937772. r=h4writer
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 20 Nov 2013 08:16:19 -0500
changeset 156874 887f595b0abbf327431e6f0461d5a8b18f027c60
parent 156873 4c981d0ac0cbe4a242f327014739fcf1f5168f88
child 156875 fa2587b098baa88948caf5d336a34619333761eb
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersh4writer
bugs939835, 937772
milestone28.0a1
Bug 939835. Fix up performance regressions from bug 937772. r=h4writer There are two changes here. 1) When trying to eliminate type barriers, skip over infallible unboxes, not just barriered ones. 2) When we statically know a value is double or int but dynamically we've only observed integers, keep allowing TI to barrier and specialize to integer instead of forcing the value to be treatd as a double.
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1571,17 +1571,17 @@ TryEliminateTypeBarrierFromTest(MTypeBar
     // The type barrier can be eliminated, however, by looking at tests
     // performed on the result of the first operation that filter out all
     // types that have been seen in the first access but not the second.
 
     // A test 'if (x.f)' filters both null and undefined.
 
     // Disregard the possible unbox added before the Typebarrier for checking.
     MDefinition *input = barrier->input();
-    if (input->isUnbox() && input->toUnbox()->mode() == MUnbox::TypeBarrier)
+    if (input->isUnbox() && input->toUnbox()->mode() != MUnbox::Fallible)
         input = input->toUnbox()->input();
 
     if (test->getOperand(0) == input && direction == TRUE_BRANCH) {
         *eliminated = true;
         barrier->replaceAllUsesWith(barrier->input());
         return;
     }
 
@@ -1621,21 +1621,18 @@ static bool
 TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
 {
     JS_ASSERT(!*eliminated);
 
     const types::TemporaryTypeSet *barrierTypes = barrier->resultTypeSet();
     const types::TemporaryTypeSet *inputTypes = barrier->input()->resultTypeSet();
 
     // Disregard the possible unbox added before the Typebarrier.
-    if (barrier->input()->isUnbox() &&
-        barrier->input()->toUnbox()->mode() == MUnbox::TypeBarrier)
-    {
+    if (barrier->input()->isUnbox() && barrier->input()->toUnbox()->mode() != MUnbox::Fallible)
         inputTypes = barrier->input()->toUnbox()->input()->resultTypeSet();
-    }
 
     if (!barrierTypes || !inputTypes)
         return true;
 
     bool filtersNull = barrierTypes->filtersType(inputTypes, types::Type::NullType());
     bool filtersUndefined = barrierTypes->filtersType(inputTypes, types::Type::UndefinedType());
 
     if (!filtersNull && !filtersUndefined)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5246,31 +5246,20 @@ IonBuilder::makeCall(JSFunction *target,
         return false;
 
     current->push(call);
     if (!resumeAfter(call))
         return false;
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
-    bool barrier = true;
-    MDefinition *replace = call;
-    if (call->isDOMFunction()) {
-        JSFunction* target = call->getSingleTarget();
-        JS_ASSERT(target && target->isNative() && target->jitInfo());
-        const JSJitInfo *jitinfo = target->jitInfo();
-        barrier = DOMCallNeedsBarrier(jitinfo, types);
-        replace = ensureDefiniteType(call, jitinfo->returnType);
-        if (replace != call) {
-            current->pop();
-            current->push(replace);
-        }
-    }
-
-    return pushTypeBarrier(replace, types, barrier);
+    if (call->isDOMFunction())
+        return pushDOMTypeBarrier(call, types, call->getSingleTarget());
+
+    return pushTypeBarrier(call, types, true);
 }
 
 bool
 IonBuilder::jsop_eval(uint32_t argc)
 {
     int calleeDepth = -((int)argc + 2);
     types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
 
@@ -6142,16 +6131,48 @@ IonBuilder::pushTypeBarrier(MDefinition 
         return pushConstant(UndefinedValue());
     if (barrier->type() == MIRType_Null)
         return pushConstant(NullValue());
 
     current->push(barrier);
     return true;
 }
 
+bool
+IonBuilder::pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func)
+{
+    JS_ASSERT(func && func->isNative() && func->jitInfo());
+
+    const JSJitInfo *jitinfo = func->jitInfo();
+    bool barrier = DOMCallNeedsBarrier(jitinfo, observed);
+    // Need to be a bit careful: if jitinfo->returnType is JSVAL_TYPE_DOUBLE but
+    // types->getKnownTypeTag() is JSVAL_TYPE_INT32, then don't unconditionally
+    // unbox as a double.  Instead, go ahead and barrier on having an int type,
+    // since we know we need a barrier anyway due to the type mismatch.  This is
+    // the only situation in which TI actually has more information about the
+    // JSValueType than codegen can, short of jitinfo->returnType just being
+    // JSVAL_TYPE_UNKNOWN.
+    MDefinition* replace = ins;
+    if (jitinfo->returnType != JSVAL_TYPE_DOUBLE ||
+        observed->getKnownTypeTag() != JSVAL_TYPE_INT32) {
+        JS_ASSERT(jitinfo->returnType == JSVAL_TYPE_UNKNOWN ||
+                  observed->getKnownTypeTag() == JSVAL_TYPE_UNKNOWN ||
+                  jitinfo->returnType == observed->getKnownTypeTag());
+        replace = ensureDefiniteType(ins, jitinfo->returnType);
+        if (replace != ins) {
+            current->pop();
+            current->push(replace);
+        }
+    } else {
+        JS_ASSERT(barrier);
+    }
+
+    return pushTypeBarrier(replace, observed, barrier);
+}
+
 MDefinition *
 IonBuilder::ensureDefiniteType(MDefinition *def, JSValueType definiteType)
 {
     MInstruction *replace;
     switch (definiteType) {
       case JSVAL_TYPE_UNDEFINED:
         def->setFoldedUnchecked();
         replace = MConstant::New(alloc(), UndefinedValue());
@@ -8336,23 +8357,18 @@ IonBuilder::getPropTryCommonGetter(bool 
     if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
         const JSJitInfo *jitinfo = commonGetter->jitInfo();
         MGetDOMProperty *get = MGetDOMProperty::New(alloc(), jitinfo, obj, guard);
         current->add(get);
         current->push(get);
 
         if (get->isEffectful() && !resumeAfter(get))
             return false;
-        bool barrier = DOMCallNeedsBarrier(jitinfo, types);
-        MDefinition *replace = ensureDefiniteType(get, jitinfo->returnType);
-        if (replace != get) {
-            current->pop();
-            current->push(replace);
-        }
-        if (!pushTypeBarrier(replace, types, barrier))
+
+        if (!pushDOMTypeBarrier(get, types, commonGetter))
             return false;
 
         *emitted = true;
         return true;
     }
 
     // Don't call the getter with a primitive value.
     if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -329,16 +329,21 @@ class IonBuilder : public MIRGenerator
 
     MConstant *constant(const Value &v);
     MConstant *constantInt(int32_t i);
 
     // Add a guard which ensure that the set of type which goes through this
     // generated code correspond to the observed types for the bytecode.
     bool pushTypeBarrier(MDefinition *def, types::TemporaryTypeSet *observed, bool needBarrier);
 
+    // As pushTypeBarrier, but will compute the needBarrier boolean itself based
+    // on observed and the JSFunction that we're planning to call. The
+    // JSFunction must be a DOM method or getter.
+    bool pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func);
+
     // If definiteType is not known or def already has the right type, just
     // returns def.  Otherwise, returns an MInstruction that has that definite
     // type, infallibly unboxing ins as needed.  The new instruction will be
     // added to |current| in this case.
     MDefinition *ensureDefiniteType(MDefinition* def, JSValueType definiteType);
 
     JSObject *getSingletonPrototype(JSFunction *target);