Bug 1041586 - Fold away property accesses to not-defined properties. r=jandem
authorTom Schuster <evilpies@gmail.com>
Thu, 14 Apr 2016 18:09:56 +0200
changeset 351765 410d733097ac4378441053cb5f4d19eb792f1b05
parent 351764 954189d579d5db542624d10c11108db60af13816
child 351766 3a15af732d22993401920ec2b95490f3ff2067f5
push id15527
push userbmo:rail@mozilla.com
push dateFri, 15 Apr 2016 01:44:41 +0000
reviewersjandem
bugs1041586
milestone48.0a1
Bug 1041586 - Fold away property accesses to not-defined properties. r=jandem
js/public/TrackedOptimizationInfo.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -11,16 +11,17 @@
 
 namespace JS {
 
 #define TRACKED_STRATEGY_LIST(_)                        \
     _(GetProp_ArgumentsLength)                          \
     _(GetProp_ArgumentsCallee)                          \
     _(GetProp_InferredConstant)                         \
     _(GetProp_Constant)                                 \
+    _(GetProp_NotDefined)                               \
     _(GetProp_StaticName)                               \
     _(GetProp_SimdGetter)                               \
     _(GetProp_TypedObject)                              \
     _(GetProp_DefiniteSlot)                             \
     _(GetProp_Unboxed)                                  \
     _(GetProp_CommonGetter)                             \
     _(GetProp_InlineAccess)                             \
     _(GetProp_Innerize)                                 \
@@ -75,16 +76,17 @@ namespace JS {
     _(UnknownProperties)                                                \
     _(Singleton)                                                        \
     _(NotSingleton)                                                     \
     _(NotFixedSlot)                                                     \
     _(InconsistentFixedSlot)                                            \
     _(NotObject)                                                        \
     _(NotStruct)                                                        \
     _(NotUnboxed)                                                       \
+    _(NotUndefined)                                                     \
     _(UnboxedConvertedToNative)                                         \
     _(StructNoField)                                                    \
     _(InconsistentFieldType)                                            \
     _(InconsistentFieldOffset)                                          \
     _(NeedsTypeBarrier)                                                 \
     _(InDictionaryMode)                                                 \
     _(NoProtoFound)                                                     \
     _(MultiProtoPaths)                                                  \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7988,16 +7988,60 @@ IonBuilder::testSingletonPropertyTypes(M
     JSObject* proto = GetBuiltinPrototypePure(&script()->global(), key);
     if (proto)
         return testSingletonProperty(proto, id);
 
     return nullptr;
 }
 
 bool
+IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id)
+{
+    TemporaryTypeSet* types = obj->resultTypeSet();
+    if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType_Object)
+        return false;
+
+    for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
+        TypeSet::ObjectKey* key = types->getObject(i);
+        if (!key)
+            continue;
+
+        while (true) {
+            if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
+                return false;
+
+            const Class* clasp = key->clasp();
+            if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, id))
+                return false;
+
+            // If the object is a singleton, we can do a lookup now to avoid
+            // unnecessary invalidations later on, in case the property types
+            // have not yet been instantiated.
+            if (key->isSingleton() &&
+                key->singleton()->is<NativeObject>() &&
+                key->singleton()->as<NativeObject>().lookupPure(id))
+            {
+                return false;
+            }
+
+            HeapTypeSetKey property = key->property(id);
+            if (property.isOwnProperty(constraints()))
+                return false;
+
+            JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
+            if (!proto)
+                break;
+            key = TypeSet::ObjectKey::get(proto);
+        }
+    }
+
+    return true;
+}
+
+bool
 IonBuilder::pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind)
 {
     MOZ_ASSERT(def == current->peek(-1));
 
     MDefinition* replace = addTypeBarrier(current->pop(), observed, kind);
     if (!replace)
         return false;
 
@@ -9014,16 +9058,23 @@ IonBuilder::getElemTryGetProp(bool* emit
 
     trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
     if (!getPropTryConstant(emitted, obj, id, types) || *emitted) {
         if (*emitted)
             index->setImplicitlyUsedUnchecked();
         return *emitted;
     }
 
+    trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
+    if (!getPropTryNotDefined(emitted, obj, id, types) || *emitted) {
+        if (*emitted)
+            index->setImplicitlyUsedUnchecked();
+        return *emitted;
+    }
+
     return true;
 }
 
 bool
 IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
 {
     MOZ_ASSERT(*emitted == false);
 
@@ -11089,16 +11140,21 @@ IonBuilder::jsop_getprop(PropertyName* n
         return emitted;
 
     if (!forceInlineCaches()) {
         // Try to hardcode known constants.
         trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
         if (!getPropTryConstant(&emitted, obj, NameToId(name), types) || emitted)
             return emitted;
 
+        // Try to hardcode known not-defined
+        trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
+        if (!getPropTryNotDefined(&emitted, obj, NameToId(name), types) || emitted)
+            return emitted;
+
         // Try to emit loads from known binary data blocks
         trackOptimizationAttempt(TrackedStrategy::GetProp_TypedObject);
         if (!getPropTryTypedObject(&emitted, obj, name) || emitted)
             return emitted;
 
         // Try to emit loads from definite slots.
         trackOptimizationAttempt(TrackedStrategy::GetProp_DefiniteSlot);
         if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted)
@@ -11331,16 +11387,40 @@ IonBuilder::getPropTryConstant(bool* emi
     pushConstant(ObjectValue(*singleton));
 
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
 
 bool
+IonBuilder::getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
+{
+    MOZ_ASSERT(*emitted == false);
+
+    if (!types->mightBeMIRType(MIRType_Undefined)) {
+        // Only optimize if we expect this property access to return undefined.
+        trackOptimizationOutcome(TrackedOutcome::NotUndefined);
+        return true;
+    }
+
+    if (!testNotDefinedProperty(obj, id)) {
+        trackOptimizationOutcome(TrackedOutcome::GenericFailure);
+        return true;
+    }
+
+    obj->setImplicitlyUsedUnchecked();
+    pushConstant(UndefinedValue());
+
+    trackOptimizationSuccess();
+    *emitted = true;
+    return true;
+}
+
+bool
 IonBuilder::getPropTryTypedObject(bool* emitted,
                                   MDefinition* obj,
                                   PropertyName* name)
 {
     TypedObjectPrediction fieldPrediction;
     size_t fieldOffset;
     size_t fieldIndex;
     if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
@@ -13436,53 +13516,18 @@ IonBuilder::inTryFold(bool* emitted, MDe
     MConstant* idConst = id->maybeConstantValue();
     jsid propId;
     if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
         return true;
 
     if (propId != IdToTypeId(propId))
         return true;
 
-    TemporaryTypeSet* types = obj->resultTypeSet();
-    if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType_Object)
-        return true;
-
-    for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
-        TypeSet::ObjectKey* key = types->getObject(i);
-        if (!key)
-            continue;
-
-        while (true) {
-            if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
-                return true;
-
-            const Class* clasp = key->clasp();
-            if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, propId))
-                return true;
-
-            // If the object is a singleton, we can do a lookup now to avoid
-            // unnecessary invalidations later on, in case the property types
-            // have not yet been instantiated.
-            if (key->isSingleton() &&
-                key->singleton()->is<NativeObject>() &&
-                key->singleton()->as<NativeObject>().lookupPure(propId))
-            {
-                return true;
-            }
-
-            HeapTypeSetKey property = key->property(propId);
-            if (property.isOwnProperty(constraints()))
-                return true;
-
-            JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
-            if (!proto)
-                break;
-            key = TypeSet::ObjectKey::get(proto);
-        }
-    }
+    if (!testNotDefinedProperty(obj, propId))
+        return true;
 
     *emitted = true;
 
     pushConstant(BooleanValue(false));
     obj->setImplicitlyUsedUnchecked();
     id->setImplicitlyUsedUnchecked();
     return true;
 }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -426,16 +426,17 @@ class IonBuilder
 
     // jsop_getprop() helpers.
     bool checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs);
     bool getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
                                     TemporaryTypeSet* types);
     bool getPropTryArgumentsLength(bool* emitted, MDefinition* obj);
     bool getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name);
     bool getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types);
+    bool getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types);
     bool getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
                                 BarrierKind barrier, TemporaryTypeSet* types);
     bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
                                    BarrierKind barrier, TemporaryTypeSet* types);
     bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
                            BarrierKind barrier, TemporaryTypeSet* types);
     bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                 TemporaryTypeSet* types);
@@ -999,16 +1000,18 @@ class IonBuilder
 
     MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);
 
     JSObject* testGlobalLexicalBinding(PropertyName* name);
 
     JSObject* testSingletonProperty(JSObject* obj, jsid id);
     JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
 
+    bool testNotDefinedProperty(MDefinition* obj, jsid id);
+
     uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
     MDefinition* convertUnboxedObjects(MDefinition* obj);
     MDefinition* convertUnboxedObjects(MDefinition* obj,
                                        const BaselineInspector::ObjectGroupVector& list);
     uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
                               JSValueType* punboxedType);
     MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                       BarrierKind barrier, TemporaryTypeSet* types);