Bug 1346028 - Support JSOP_INITELEM_INC in Ion draft
authorTed Campbell <tcampbell@mozilla.com>
Mon, 20 Mar 2017 18:30:26 -0400
changeset 504684 05794e48e2af77f59d5188956550f15aa30f73ee
parent 501881 c5dc73242e399545d80cb075490058cc3a630e14
child 550702 28035411ebd5327a5e58fed6fbba451418fa954a
push id50849
push userbmo:tcampbell@mozilla.com
push dateFri, 24 Mar 2017 15:32:02 +0000
bugs1346028
milestone55.0a1
Bug 1346028 - Support JSOP_INITELEM_INC in Ion MozReview-Commit-ID: 3orllfJUDze
js/public/TrackedOptimizationInfo.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -141,16 +141,17 @@ namespace JS {
     _(SpeculationOnInputTypesFailed)                                    \
     _(RelationalCompare)                                                \
     _(OperandTypeNotBitwiseComparable)                                  \
     _(OperandMaybeEmulatesUndefined)                                    \
     _(LoosyUndefinedNullCompare)                                        \
     _(LoosyInt32BooleanCompare)                                         \
     _(CallsValueOf)                                                     \
     _(StrictCompare)                                                    \
+    _(InitHole)                                                         \
                                                                         \
     _(ICOptStub_GenericSuccess)                                         \
                                                                         \
     _(ICGetPropStub_ReadSlot)                                           \
     _(ICGetPropStub_CallGetter)                                         \
     _(ICGetPropStub_ArrayLength)                                        \
     _(ICGetPropStub_UnboxedRead)                                        \
     _(ICGetPropStub_UnboxedReadExpando)                                 \
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2977,16 +2977,22 @@ BaselineCompiler::emit_JSOP_INITELEM_INC
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Pop the rhs
     frame.pop();
 
     // Increment index
     Address indexAddr = frame.addressOfStackValue(frame.peek(-1));
+#ifdef DEBUG
+    Label isInt32;
+    masm.branchTestInt32(Assembler::Equal, indexAddr, &isInt32);
+    masm.assumeUnreachable("INITELEM_INC index must be Int32");
+    masm.bind(&isInt32);
+#endif
     masm.incrementInt32Value(indexAddr);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_GETLOCAL()
 {
     frame.pushLocal(GET_LOCALNO(pc));
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1968,16 +1968,19 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_NEWOBJECT:
         return jsop_newobject();
 
       case JSOP_INITELEM:
       case JSOP_INITHIDDENELEM:
         return jsop_initelem();
 
+      case JSOP_INITELEM_INC:
+        return jsop_initelem_inc();
+
       case JSOP_INITELEM_ARRAY:
         return jsop_initelem_array();
 
       case JSOP_INITPROP:
       case JSOP_INITLOCKEDPROP:
       case JSOP_INITHIDDENPROP:
       {
         PropertyName* name = info().getAtom(pc)->asPropertyName();
@@ -6075,33 +6078,61 @@ IonBuilder::jsop_newobject()
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_initelem()
 {
     MOZ_ASSERT(*pc == JSOP_INITELEM || *pc == JSOP_INITHIDDENELEM);
 
     MDefinition* value = current->pop();
     MDefinition* id = current->pop();
-    MDefinition* obj = current->pop();
+    MDefinition* obj = current->peek(-1);
 
     bool emitted = false;
 
     if (!forceInlineCaches() && *pc == JSOP_INITELEM) {
         MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
         if (emitted)
             return Ok();
     }
 
     MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
     if (emitted)
         return Ok();
 
     MInitElem* initElem = MInitElem::New(alloc(), obj, id, value);
     current->add(initElem);
-    current->push(obj);
+
+    return resumeAfter(initElem);
+}
+
+AbortReasonOr<Ok>
+IonBuilder::jsop_initelem_inc()
+{
+    MDefinition* value = current->pop();
+    MDefinition* id = current->pop();
+    MDefinition* obj = current->peek(-1);
+
+    bool emitted = false;
+
+    MAdd* nextId = MAdd::New(alloc(), id, constantInt(1), MIRType::Int32);
+    current->add(nextId);
+    current->push(nextId);
+
+    if (!forceInlineCaches()) {
+        MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
+        if (emitted)
+            return Ok();
+    }
+
+    MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
+    if (emitted)
+        return Ok();
+
+    MCallInitElementArray* initElem = MCallInitElementArray::New(alloc(), obj, id, value);
+    current->add(initElem);
 
     return resumeAfter(initElem);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_initelem_array()
 {
     MDefinition* value = current->pop();
@@ -8926,16 +8957,22 @@ IonBuilder::setElemTryTypedArray(bool* e
 }
 
 AbortReasonOr<Ok>
 IonBuilder::initOrSetElemTryDense(bool* emitted, MDefinition* object,
                                   MDefinition* index, MDefinition* value, bool writeHole)
 {
     MOZ_ASSERT(*emitted == false);
 
+    if (value->type() == MIRType::MagicHole)
+    {
+        trackOptimizationOutcome(TrackedOutcome::InitHole);
+        return Ok();
+    }
+
     JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
     if (unboxedType == JSVAL_TYPE_MAGIC) {
         if (!ElementAccessIsDenseNative(constraints(), object, index)) {
             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
             return Ok();
         }
     }
 
@@ -8997,31 +9034,35 @@ IonBuilder::setElemTryArguments(bool* em
 }
 
 AbortReasonOr<Ok>
 IonBuilder::initOrSetElemTryCache(bool* emitted, MDefinition* object,
                                   MDefinition* index, MDefinition* value)
 {
     MOZ_ASSERT(*emitted == false);
 
-    MDefinition* objectArg = object;
-
     if (!object->mightBeType(MIRType::Object)) {
         trackOptimizationOutcome(TrackedOutcome::NotObject);
         return Ok();
     }
 
     if (!index->mightBeType(MIRType::Int32) &&
         !index->mightBeType(MIRType::String) &&
         !index->mightBeType(MIRType::Symbol))
     {
         trackOptimizationOutcome(TrackedOutcome::IndexType);
         return Ok();
     }
 
+    if (value->type() == MIRType::MagicHole)
+    {
+        trackOptimizationOutcome(TrackedOutcome::InitHole);
+        return Ok();
+    }
+
     bool barrier = true;
     bool indexIsInt32 = index->type() == MIRType::Int32;
 
     if (indexIsInt32 &&
         !PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                        &object, nullptr, &value, /* canModify = */ true))
     {
         barrier = false;
@@ -9041,37 +9082,34 @@ IonBuilder::initOrSetElemTryCache(bool* 
 
     // Emit SetPropertyCache.
     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
     MSetPropertyCache* ins =
         MSetPropertyCache::New(alloc(), object, index, value, strict, NeedsPostBarrier(value),
                                barrier, guardHoles);
     current->add(ins);
 
-    if (IsPropertyInitOp(JSOp(*pc)))
-        current->push(objectArg);
-    else
+    // Push value back onto stack. Init ops keep their object on stack.
+    if (!IsPropertyInitOp(JSOp(*pc)))
         current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
                                MDefinition* obj, MDefinition* id, MDefinition* value,
                                JSValueType unboxedType, bool writeHole, bool* emitted)
 {
     MOZ_ASSERT(*emitted == false);
 
-    MDefinition* objArg = obj;
-
     MIRType elementType = MIRType::None;
     if (unboxedType == JSVAL_TYPE_MAGIC)
         elementType = DenseNativeElementType(constraints(), obj);
     bool packed = ElementAccessIsPacked(constraints(), obj);
 
     // Writes which are on holes in the object do not have to bail out if they
     // cannot hit another indexed property on the object or its prototypes.
     bool hasExtraIndexedProperty;
@@ -9165,19 +9203,18 @@ IonBuilder::initOrSetElemDense(Temporary
             MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
             store = ins;
             common = ins;
 
             current->add(store);
         }
     }
 
-    if (IsPropertyInitOp(JSOp(*pc)))
-        current->push(objArg);
-    else
+    // Push value back onto stack. Init ops keep their object on stack.
+    if (!IsPropertyInitOp(JSOp(*pc)))
         current->push(value);
 
     MOZ_TRY(resumeAfter(store));
 
     if (common) {
         // Determine whether a write barrier is required.
         if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
             common->setNeedsBarrier();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -547,16 +547,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_setprop(PropertyName* name);
     AbortReasonOr<Ok> jsop_delprop(PropertyName* name);
     AbortReasonOr<Ok> jsop_delelem();
     AbortReasonOr<Ok> jsop_newarray(uint32_t length);
     AbortReasonOr<Ok> jsop_newarray(JSObject* templateObject, uint32_t length);
     AbortReasonOr<Ok> jsop_newarray_copyonwrite();
     AbortReasonOr<Ok> jsop_newobject();
     AbortReasonOr<Ok> jsop_initelem();
+    AbortReasonOr<Ok> jsop_initelem_inc();
     AbortReasonOr<Ok> jsop_initelem_array();
     AbortReasonOr<Ok> jsop_initelem_getter_setter();
     AbortReasonOr<Ok> jsop_mutateproto();
     AbortReasonOr<Ok> jsop_initprop(PropertyName* name);
     AbortReasonOr<Ok> jsop_initprop_getter_setter(PropertyName* name);
     AbortReasonOr<Ok> jsop_regexp(RegExpObject* reobj);
     AbortReasonOr<Ok> jsop_object(JSObject* obj);
     AbortReasonOr<Ok> jsop_lambda(JSFunction* fun);