Bug 1404659 - Constant fold in/hasOwn with a definite slot or known unboxed offset. r=jandem
authorTom Schuster <evilpies@gmail.com>
Fri, 06 Oct 2017 17:45:52 +0200
changeset 676127 d1a430da8fe8332956816c94027947a07805fc8a
parent 676126 3edd962139b648d3b8b22e70d5a17af3414de6e4
child 676128 d252581d930e60eb0be420c77aeaa402d4b9d25e
push id83398
push userbmo:rail@mozilla.com
push dateFri, 06 Oct 2017 17:12:44 +0000
reviewersjandem
bugs1404659
milestone58.0a1
Bug 1404659 - Constant fold in/hasOwn with a definite slot or known unboxed offset. r=jandem
js/src/jit-test/tests/ion/has-definite-folding.js
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/has-definite-folding.js
@@ -0,0 +1,32 @@
+var max = 40;
+setJitCompilerOption("ion.warmup.trigger", max - 10);
+
+function defineProperty() {
+    var abc = {};
+    Object.defineProperty(abc, "x", {value: 1})
+    assertEq(abc.x, 1);
+}
+
+function simple() {
+    var o = {a: 1};
+    assertEq("a" in o, true);
+    assertEq("b" in o, false);
+    assertEq(o.hasOwnProperty("a"), true);
+    assertEq(o.hasOwnProperty("b"), false);
+}
+
+function proto() {
+    var o = {a: 1, __proto__: {b: 2}};
+    assertEq("a" in o, true);
+    assertEq("b" in o, true);
+    assertEq("c" in o, false);
+    assertEq(o.hasOwnProperty("a"), true);
+    assertEq(o.hasOwnProperty("b"), false);
+    assertEq(o.hasOwnProperty("c"), false);
+}
+
+for (var i = 0; i < max; i++) {
+    defineProperty();
+    simple();
+    proto();
+}
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9637,17 +9637,17 @@ IonBuilder::jsop_checkobjcoercible()
     // it should be OK.
     MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop());
     current->add(check);
     current->push(check);
     return resumeAfter(check);
 }
 
 uint32_t
-IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed)
+IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, jsid id, uint32_t* pnfixed)
 {
     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
         return UINT32_MAX;
     }
 
     uint32_t slot = UINT32_MAX;
 
@@ -9661,17 +9661,17 @@ IonBuilder::getDefiniteSlot(TemporaryTyp
             return UINT32_MAX;
         }
 
         if (key->isSingleton()) {
             trackOptimizationOutcome(TrackedOutcome::Singleton);
             return UINT32_MAX;
         }
 
-        HeapTypeSetKey property = key->property(NameToId(name));
+        HeapTypeSetKey property = key->property(id);
         if (!property.maybeTypes() ||
             !property.maybeTypes()->definiteProperty() ||
             property.nonData(constraints()))
         {
             trackOptimizationOutcome(TrackedOutcome::NotFixedSlot);
             return UINT32_MAX;
         }
 
@@ -9691,17 +9691,17 @@ IonBuilder::getDefiniteSlot(TemporaryTyp
             return UINT32_MAX;
         }
     }
 
     return slot;
 }
 
 uint32_t
-IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType)
+IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, jsid id, JSValueType* punboxedType)
 {
     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
         return UINT32_MAX;
     }
 
     uint32_t offset = UINT32_MAX;
 
@@ -9721,17 +9721,17 @@ IonBuilder::getUnboxedOffset(TemporaryTy
         }
 
         UnboxedLayout* layout = key->group()->maybeUnboxedLayout();
         if (!layout) {
             trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
             return UINT32_MAX;
         }
 
-        const UnboxedLayout::Property* property = layout->lookup(name);
+        const UnboxedLayout::Property* property = layout->lookup(id);
         if (!property) {
             trackOptimizationOutcome(TrackedOutcome::StructNoField);
             return UINT32_MAX;
         }
 
         if (layout->nativeGroup()) {
             trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
             return UINT32_MAX;
@@ -10706,17 +10706,17 @@ IonBuilder::convertUnboxedObjects(MDefin
 
 AbortReasonOr<Ok>
 IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
                                    BarrierKind barrier, TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     uint32_t nfixed;
-    uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed);
+    uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
     if (slot == UINT32_MAX)
         return Ok();
 
     if (obj->type() != MIRType::Object) {
         MGuardObject* guard = MGuardObject::New(alloc(), obj);
         current->add(guard);
         obj = guard;
     }
@@ -10854,17 +10854,17 @@ IonBuilder::loadUnboxedValue(MDefinition
 
 AbortReasonOr<Ok>
 IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
                               BarrierKind barrier, TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     JSValueType unboxedType;
-    uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
+    uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
     if (offset == UINT32_MAX)
         return Ok();
 
     if (obj->type() != MIRType::Object) {
         MGuardObject* guard = MGuardObject::New(alloc(), obj);
         current->add(guard);
         obj = guard;
     }
@@ -11716,17 +11716,17 @@ IonBuilder::setPropTryDefiniteSlot(bool*
     MOZ_ASSERT(*emitted == false);
 
     if (barrier) {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return Ok();
     }
 
     uint32_t nfixed;
-    uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed);
+    uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
     if (slot == UINT32_MAX)
         return Ok();
 
     bool writeBarrier = false;
     for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) {
         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
         if (!key)
             continue;
@@ -11834,17 +11834,17 @@ IonBuilder::setPropTryUnboxed(bool* emit
     MOZ_ASSERT(*emitted == false);
 
     if (barrier) {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return Ok();
     }
 
     JSValueType unboxedType;
-    uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
+    uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
     if (offset == UINT32_MAX)
         return Ok();
 
     if (obj->type() != MIRType::Object) {
         MGuardObject* guard = MGuardObject::New(alloc(), obj);
         current->add(guard);
         obj = guard;
     }
@@ -12725,16 +12725,20 @@ IonBuilder::jsop_in()
 
         MOZ_TRY(inTryDense(&emitted, obj, id));
         if (emitted)
             return Ok();
 
         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ false));
         if (emitted)
             return Ok();
+
+        MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
+        if (emitted)
+            return Ok();
     }
 
     MInCache* ins = MInCache::New(alloc(), id, obj);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
@@ -12812,26 +12816,68 @@ IonBuilder::hasTryNotDefined(bool* emitt
 
     pushConstant(BooleanValue(false));
     obj->setImplicitlyUsedUnchecked();
     id->setImplicitlyUsedUnchecked();
     return Ok();
 }
 
 AbortReasonOr<Ok>
+IonBuilder::hasTryDefiniteSlotOrUnboxed(bool* emitted, MDefinition* obj, MDefinition* id)
+{
+    // Fold |id in obj| to |true|, when obj definitely contains a property with
+    // that name.
+    MOZ_ASSERT(!*emitted);
+
+    if (obj->type() != MIRType::Object)
+        return Ok();
+
+    MConstant* idConst = id->maybeConstantValue();
+    jsid propId;
+    if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
+        return Ok();
+
+    if (propId != IdToTypeId(propId))
+        return Ok();
+
+    // Try finding a native definite slot first.
+    uint32_t nfixed;
+    uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), propId, &nfixed);
+    if (slot == UINT32_MAX) {
+        // Check for unboxed object properties next.
+        JSValueType unboxedType;
+        uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), propId, &unboxedType);
+        if (offset == UINT32_MAX)
+            return Ok();
+    }
+
+    *emitted = true;
+
+    pushConstant(BooleanValue(true));
+    obj->setImplicitlyUsedUnchecked();
+    id->setImplicitlyUsedUnchecked();
+    return Ok();
+}
+
+AbortReasonOr<Ok>
 IonBuilder::jsop_hasown()
 {
     MDefinition* obj = convertUnboxedObjects(current->pop());
     MDefinition* id = current->pop();
 
     if (!forceInlineCaches()) {
         bool emitted = false;
+
         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ true));
         if (emitted)
             return Ok();
+
+        MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
+        if (emitted)
+            return Ok();
     }
 
     MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
     return Ok();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -341,16 +341,17 @@ class IonBuilder
     // jsop_newobject helpers.
     AbortReasonOr<Ok> newObjectTrySharedStub(bool* emitted);
     AbortReasonOr<Ok> newObjectTryTemplateObject(bool* emitted, JSObject* templateObject);
     AbortReasonOr<Ok> newObjectTryVM(bool* emitted, JSObject* templateObject);
 
     // jsop_in/jsop_hasown helpers.
     AbortReasonOr<Ok> inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
     AbortReasonOr<Ok> hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty);
+    AbortReasonOr<Ok> hasTryDefiniteSlotOrUnboxed(bool* emitted, MDefinition* obj, MDefinition* id);
 
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
     bool typedObjectHasField(MDefinition* typedObj,
                              PropertyName* name,
                              size_t* fieldOffset,
                              TypedObjectPrediction* fieldTypeReprs,
@@ -865,21 +866,21 @@ class IonBuilder
 
     JSObject* testGlobalLexicalBinding(PropertyName* name);
 
     JSObject* testSingletonProperty(JSObject* obj, jsid id);
     JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);
 
     AbortReasonOr<bool> testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty = false);
 
-    uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
+    uint32_t getDefiniteSlot(TemporaryTypeSet* types, jsid id, uint32_t* pnfixed);
     MDefinition* convertUnboxedObjects(MDefinition* obj);
     MDefinition* convertUnboxedObjects(MDefinition* obj,
                                        const BaselineInspector::ObjectGroupVector& list);
-    uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
+    uint32_t getUnboxedOffset(TemporaryTypeSet* types, jsid id,
                               JSValueType* punboxedType);
     MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                       BarrierKind barrier, TemporaryTypeSet* types);
     MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
                                    MDefinition* scaledOffset, JSValueType unboxedType,
                                    BarrierKind barrier, TemporaryTypeSet* types);
     MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                        MDefinition* value);