Bug 1344463 - Optimize JSOP_INITELEM in Ion and emit it for 3-arguments _DefineDataProperty in self-hosted code. r=till,evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 04 Mar 2017 15:24:44 +0100
changeset 346038 c87ea81036b79a6f852d9e62659d922fa6d387d1
parent 346037 a1535bb7d31397edde81076bd137d344593dcd9f
child 346039 63d05802b98a6d251937b9965294a290c63ea7f5
push id38387
push usercbook@mozilla.com
push dateMon, 06 Mar 2017 10:11:11 +0000
treeherderautoland@937f89775395 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, evilpie
bugs1344463
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1344463 - Optimize JSOP_INITELEM in Ion and emit it for 3-arguments _DefineDataProperty in self-hosted code. r=till,evilpie
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit-test/tests/modules/bug-1247934.js
js/src/jit/CacheIR.cpp
js/src/jit/InlinableNatives.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonIC.cpp
js/src/jit/MCallOptimize.cpp
js/src/vm/CommonPropertyNames.h
js/src/vm/SelfHosting.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -8980,16 +8980,45 @@ BytecodeEmitter::emitSelfHostedAllowCont
         return false;
     }
 
     // We're just here as a sentinel. Pass the value through directly.
     return emitTree(pn->pn_head->pn_next);
 }
 
 bool
+BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
+{
+    // Only optimize when 3 arguments are passed (we use 4 to include |this|).
+    MOZ_ASSERT(pn->pn_count == 4);
+
+    ParseNode* funNode = pn->pn_head;  // The _DefineDataProperty node.
+
+    ParseNode* objNode = funNode->pn_next;
+    if (!emitTree(objNode))
+        return false;
+
+    ParseNode* idNode = objNode->pn_next;
+    if (!emitTree(idNode))
+        return false;
+
+    ParseNode* valNode = idNode->pn_next;
+    if (!emitTree(valNode))
+        return false;
+
+    // This will leave the object on the stack instead of pushing |undefined|,
+    // but that's fine because the self-hosted code doesn't use the return
+    // value.
+    if (!emit1(JSOP_INITELEM))
+        return false;
+
+    return true;
+}
+
+bool
 BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result)
 {
     if (!sc->isFunctionBox()) {
         *result = false;
         return true;
     }
 
     FunctionBox* funbox = sc->asFunctionBox();
@@ -9107,16 +9136,18 @@ BytecodeEmitter::emitCallOrNew(ParseNode
                 return emitSelfHostedCallFunction(pn);
             }
             if (pn2->name() == cx->names().resumeGenerator)
                 return emitSelfHostedResumeGenerator(pn);
             if (pn2->name() == cx->names().forceInterpreter)
                 return emitSelfHostedForceInterpreter(pn);
             if (pn2->name() == cx->names().allowContentIter)
                 return emitSelfHostedAllowContentIter(pn);
+            if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
+                return emitSelfHostedDefineDataProperty(pn);
             // Fall through.
         }
         if (!emitGetName(pn2, callop))
             return false;
         break;
       case PNK_DOT:
         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
         if (pn2->as<PropertyAccess>().isSuper()) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -737,16 +737,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
     MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
 
     MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
+    MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
 
     MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor);
     MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn);
     MOZ_MUST_USE bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope);
     MOZ_MUST_USE bool emitComprehensionForOf(ParseNode* pn);
 
     MOZ_MUST_USE bool emitDo(ParseNode* pn);
     MOZ_MUST_USE bool emitWhile(ParseNode* pn);
--- a/js/src/jit-test/tests/modules/bug-1247934.js
+++ b/js/src/jit-test/tests/modules/bug-1247934.js
@@ -1,10 +1,8 @@
-// |jit-test| --unboxed-arrays
-
 let moduleRepo = {};
 setModuleResolveHook(function(module, specifier) {
         return moduleRepo[specifier];
 });
 setJitCompilerOption("ion.warmup.trigger", 50);
 s = "";
 for (i = 0; i < 1024; i++) s += "export let e" + i + "\n";
 moduleRepo['a'] = parseModule(s);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2000,29 +2000,33 @@ SetPropIRGenerator::tryAttachStub()
         ObjOperandId objId = writer.guardIsObject(objValId);
         if (nameOrSymbol) {
             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
                 return true;
-            if (tryAttachSetter(obj, objId, id, rhsValId))
-                return true;
             if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
                 return true;
-            if (tryAttachProxy(obj, objId, id, rhsValId))
-                return true;
+            if (IsPropertySetOp(JSOp(*pc_))) {
+                if (tryAttachSetter(obj, objId, id, rhsValId))
+                    return true;
+                if (tryAttachProxy(obj, objId, id, rhsValId))
+                    return true;
+            }
             return false;
         }
 
-        if (tryAttachProxyElement(obj, objId, rhsValId))
-            return true;
+        if (IsPropertySetOp(JSOp(*pc_))) {
+            if (tryAttachProxyElement(obj, objId, rhsValId))
+                return true;
+        }
 
         uint32_t index;
         Int32OperandId indexId;
         if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
             if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
                 return true;
             if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId))
                 return true;
@@ -2274,18 +2278,16 @@ SetPropIRGenerator::trackNotAttached()
 }
 
 static bool
 CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
                 MutableHandleObject holder, MutableHandleShape propShape,
                 bool* isTemporarilyUnoptimizable)
 {
     // Don't attach a setter stub for ops like JSOP_INITELEM.
-    if (IsPropertyInitOp(JSOp(*pc)))
-        return false;
     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
 
     PropertyResult prop;
     if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
         return false;
 
     if (prop.isNonNativeProperty())
         return false;
@@ -2719,16 +2721,19 @@ SetPropIRGenerator::tryAttachDOMProxyUns
     trackAttached("DOMProxyUnshadowed");
     return true;
 }
 
 bool
 SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
                                    ValOperandId rhsId)
 {
+    // Don't attach a setter stub for ops like JSOP_INITELEM.
+    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
+
     switch (GetProxyStubType(cx_, obj, id)) {
       case ProxyStubType::None:
         return false;
       case ProxyStubType::DOMExpando:
       case ProxyStubType::DOMShadowed:
         return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
       case ProxyStubType::DOMUnshadowed:
         if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
@@ -2739,16 +2744,19 @@ SetPropIRGenerator::tryAttachProxy(Handl
     }
 
     MOZ_CRASH("Unexpected ProxyStubType");
 }
 
 bool
 SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
 {
+    // Don't attach a setter stub for ops like JSOP_INITELEM.
+    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
+
     if (!obj->is<ProxyObject>())
         return false;
 
     writer.guardIsProxy(objId);
 
     // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
     // proxies here as we don't have specialized DOM stubs for this.
     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -114,17 +114,16 @@
     _(IntrinsicIsConstructor)       \
     _(IntrinsicToObject)            \
     _(IntrinsicIsObject)            \
     _(IntrinsicIsWrappedArrayConstructor) \
     _(IntrinsicToInteger)           \
     _(IntrinsicToString)            \
     _(IntrinsicIsConstructing)      \
     _(IntrinsicSubstringKernel)     \
-    _(IntrinsicDefineDataProperty)  \
     _(IntrinsicObjectHasPrototype)  \
                                     \
     _(IntrinsicIsArrayIterator)     \
     _(IntrinsicIsMapIterator)       \
     _(IntrinsicIsSetIterator)       \
     _(IntrinsicIsStringIterator)    \
                                     \
     _(IntrinsicGetNextMapEntryForIterator) \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6050,22 +6050,37 @@ IonBuilder::jsop_newobject()
         return Ok();
 
     MOZ_CRASH("newobject should have been emited");
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_initelem()
 {
+    MOZ_ASSERT(*pc == JSOP_INITELEM || *pc == JSOP_INITHIDDENELEM);
+
     MDefinition* value = current->pop();
     MDefinition* id = current->pop();
-    MDefinition* obj = current->peek(-1);
+    MDefinition* obj = current->pop();
+
+    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_array()
 {
     MDefinition* value = current->pop();
@@ -8674,17 +8689,17 @@ IonBuilder::jsop_setelem()
         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray);
         MOZ_TRY(setElemTryTypedArray(&emitted, object, index, value));
         if (emitted)
             return Ok();
 
         trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
         SetElemICInspector icInspect(inspector->setElemICInspector(pc));
         bool writeHole = icInspect.sawOOBDenseWrite();
-        MOZ_TRY(setElemTryDense(&emitted, object, index, value, writeHole));
+        MOZ_TRY(initOrSetElemTryDense(&emitted, object, index, value, writeHole));
         if (emitted)
             return Ok();
 
         trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
         MOZ_TRY(setElemTryArguments(&emitted, object, index, value));
         if (emitted)
             return Ok();
     }
@@ -8692,17 +8707,17 @@ IonBuilder::jsop_setelem()
     if (script()->argumentsHasVarBinding() &&
         object->mightBeType(MIRType::MagicOptimizedArguments) &&
         info().analysisMode() != Analysis_ArgumentsUsage)
     {
         return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
     }
 
     trackOptimizationAttempt(TrackedStrategy::SetElem_InlineCache);
-    MOZ_TRY(setElemTryCache(&emitted, object, index, value));
+    MOZ_TRY(initOrSetElemTryCache(&emitted, object, index, value));
     if (emitted)
         return Ok();
 
     // Emit call.
     MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
     current->add(ins);
     current->push(value);
 
@@ -8880,18 +8895,18 @@ IonBuilder::setElemTryTypedArray(bool* e
     MOZ_TRY(jsop_setelem_typed(arrayType, object, index, value));
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
-IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
-                            MDefinition* index, MDefinition* value, bool writeHole)
+IonBuilder::initOrSetElemTryDense(bool* emitted, MDefinition* object,
+                                  MDefinition* index, MDefinition* value, bool writeHole)
 {
     MOZ_ASSERT(*emitted == false);
 
     JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
     if (unboxedType == JSVAL_TYPE_MAGIC) {
         if (!ElementAccessIsDenseNative(constraints(), object, index)) {
             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
             return Ok();
@@ -8926,17 +8941,17 @@ IonBuilder::setElemTryDense(bool* emitte
     bool hasExtraIndexedProperty;
     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, object));
     if (hasExtraIndexedProperty && failedBoundsCheck_) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return Ok();
     }
 
     // Emit dense setelem variant.
-    MOZ_TRY(jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted));
+    MOZ_TRY(initOrSetElemDense(conversion, object, index, value, unboxedType, writeHole, emitted));
 
     if (!*emitted) {
         trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
         return Ok();
     }
 
     trackOptimizationSuccess();
     return Ok();
@@ -8951,21 +8966,23 @@ IonBuilder::setElemTryArguments(bool* em
     if (object->type() != MIRType::MagicOptimizedArguments)
         return Ok();
 
     // Arguments are not supported yet.
     return abort(AbortReason::Disable, "NYI arguments[]=");
 }
 
 AbortReasonOr<Ok>
-IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
-                            MDefinition* index, MDefinition* value)
+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))
@@ -9003,32 +9020,38 @@ IonBuilder::setElemTryCache(bool* emitte
             current->add(MPostWriteBarrier::New(alloc(), object, value));
     }
 
     // Emit SetPropertyCache.
     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
     MSetPropertyCache* ins =
         MSetPropertyCache::New(alloc(), object, index, value, strict, barrier, guardHoles);
     current->add(ins);
-    current->push(value);
+
+    if (IsPropertyInitOp(JSOp(*pc)))
+        current->push(objectArg);
+    else
+        current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
-IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
+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;
@@ -9094,47 +9117,48 @@ IonBuilder::jsop_setelem_dense(Temporary
     MInstruction* store;
     MStoreElementCommon* common = nullptr;
     if (writeHole && !hasExtraIndexedProperty && !mayBeFrozen) {
         MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
         store = ins;
         common = ins;
 
         current->add(ins);
-        current->push(value);
     } else if (mayBeFrozen) {
         MOZ_ASSERT(!hasExtraIndexedProperty,
                    "FallibleStoreElement codegen assumes no extra indexed properties");
 
         bool strict = IsStrictSetPC(pc);
         MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
                                                                 newValue, unboxedType, strict);
         store = ins;
         common = ins;
 
         current->add(ins);
-        current->push(value);
     } else {
         MInstruction* initLength = initializedLength(obj, elements, unboxedType);
 
         id = addBoundsCheck(id, initLength);
         bool needsHoleCheck = !packed && hasExtraIndexedProperty;
 
         if (unboxedType != JSVAL_TYPE_MAGIC) {
             store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
         } else {
             MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
             store = ins;
             common = ins;
 
             current->add(store);
         }
-
+    }
+
+    if (IsPropertyInitOp(JSOp(*pc)))
+        current->push(objArg);
+    else
         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
@@ -390,22 +390,23 @@ class IonBuilder
 
     // jsop_setelem() helpers.
     AbortReasonOr<Ok> setElemTryTypedArray(bool* emitted, MDefinition* object,
                                            MDefinition* index, MDefinition* value);
     AbortReasonOr<Ok> setElemTryTypedObject(bool* emitted, MDefinition* obj,
                                             MDefinition* index, MDefinition* value);
     AbortReasonOr<Ok> setElemTryTypedStatic(bool* emitted, MDefinition* object,
                                             MDefinition* index, MDefinition* value);
-    AbortReasonOr<Ok> setElemTryDense(bool* emitted, MDefinition* object,
-                                      MDefinition* index, MDefinition* value, bool writeHole);
+    AbortReasonOr<Ok> initOrSetElemTryDense(bool* emitted, MDefinition* object,
+                                            MDefinition* index, MDefinition* value,
+                                            bool writeHole);
     AbortReasonOr<Ok> setElemTryArguments(bool* emitted, MDefinition* object,
                                           MDefinition* index, MDefinition* value);
-    AbortReasonOr<Ok> setElemTryCache(bool* emitted, MDefinition* object,
-                                      MDefinition* index, MDefinition* value);
+    AbortReasonOr<Ok> initOrSetElemTryCache(bool* emitted, MDefinition* object,
+                                            MDefinition* index, MDefinition* value);
     AbortReasonOr<Ok> setElemTryReferenceElemOfTypedObject(bool* emitted,
                                                            MDefinition* obj,
                                                            MDefinition* index,
                                                            TypedObjectPrediction objPrediction,
                                                            MDefinition* value,
                                                            TypedObjectPrediction elemPrediction);
     AbortReasonOr<Ok> setElemTryScalarElemOfTypedObject(bool* emitted,
                                                         MDefinition* obj,
@@ -523,17 +524,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_bindname(PropertyName* name);
     AbortReasonOr<Ok> jsop_bindvar();
     AbortReasonOr<Ok> jsop_getelem();
     AbortReasonOr<Ok> jsop_getelem_dense(MDefinition* obj, MDefinition* index,
                                          JSValueType unboxedType);
     AbortReasonOr<Ok> jsop_getelem_typed(MDefinition* obj, MDefinition* index,
                                          ScalarTypeDescr::Type arrayType);
     AbortReasonOr<Ok> jsop_setelem();
-    AbortReasonOr<Ok> jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
+    AbortReasonOr<Ok> initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
                                          MDefinition* object, MDefinition* index,
                                          MDefinition* value, JSValueType unboxedType,
                                          bool writeHole, bool* emitted);
     AbortReasonOr<Ok> jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
                                          MDefinition* object, MDefinition* index,
                                          MDefinition* value);
     AbortReasonOr<Ok> jsop_length();
     bool jsop_length_fastPath();
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -228,23 +228,29 @@ IonSetPropertyIC::update(JSContext* cx, 
         SetPropIRGenerator gen(cx, script, pc, ic->kind(), &isTemporarilyUnoptimizable,
                                objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
         if (gen.tryAttachStub()) {
             attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                              ionScript, gen.typeCheckInfo());
         }
     }
 
+    jsbytecode* pc = ic->pc();
     if (ic->kind() == CacheKind::SetElem) {
-        if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict()))
-            return false;
+        if (IsPropertyInitOp(JSOp(*pc))) {
+            if (!InitElemOperation(cx, pc, obj, idVal, rhs))
+                return false;
+        } else {
+            MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
+            if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict()))
+                return false;
+        }
     } else {
         MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
 
-        jsbytecode* pc = ic->pc();
         if (*pc == JSOP_INITGLEXICAL) {
             RootedScript script(cx, ic->script());
             MOZ_ASSERT(!script->hasNonSyntacticScope());
             InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(), script, pc, rhs);
         } else {
             RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
             if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc))
                 return false;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -294,18 +294,16 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::IntrinsicIsArrayIterator:
         return inlineHasClass(callInfo, &ArrayIteratorObject::class_);
       case InlinableNative::IntrinsicIsMapIterator:
         return inlineHasClass(callInfo, &MapIteratorObject::class_);
       case InlinableNative::IntrinsicIsSetIterator:
         return inlineHasClass(callInfo, &SetIteratorObject::class_);
       case InlinableNative::IntrinsicIsStringIterator:
         return inlineHasClass(callInfo, &StringIteratorObject::class_);
-      case InlinableNative::IntrinsicDefineDataProperty:
-        return inlineDefineDataProperty(callInfo);
       case InlinableNative::IntrinsicObjectHasPrototype:
         return inlineObjectHasPrototype(callInfo);
 
       // Map intrinsics.
       case InlinableNative::IntrinsicGetNextMapEntryForIterator:
         return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Map);
 
       // Set intrinsics.
@@ -2117,48 +2115,16 @@ IonBuilder::inlineObjectCreate(CallInfo&
     bool emitted = false;
     MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
 
     MOZ_ASSERT(emitted);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
-IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
-{
-    MOZ_ASSERT(!callInfo.constructing());
-
-    // Only handle definitions of plain data properties.
-    if (callInfo.argc() != 3)
-        return InliningStatus_NotInlined;
-
-    MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0));
-    MDefinition* id = callInfo.getArg(1);
-    MDefinition* value = callInfo.getArg(2);
-
-    bool hasExtraIndexedProperty;
-    MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
-    if (hasExtraIndexedProperty)
-        return InliningStatus_NotInlined;
-
-    // setElemTryDense will push the value as the result of the define instead
-    // of |undefined|, but this is fine if the rval is ignored (as it should be
-    // in self hosted code.)
-    MOZ_ASSERT(*GetNextPc(pc) == JSOP_POP);
-
-    bool emitted = false;
-    MOZ_TRY(setElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
-    if (!emitted)
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningResult
 IonBuilder::inlineHasClass(CallInfo& callInfo,
                            const Class* clasp1, const Class* clasp2,
                            const Class* clasp3, const Class* clasp4)
 {
     if (callInfo.constructing() || callInfo.argc() != 1) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -395,10 +395,11 @@
     macro(undefined, undefined, "undefined") \
     macro(object, object, "object") \
     macro(function, function, "function") \
     macro(string, string, "string") \
     macro(number, number, "number") \
     macro(boolean, boolean, "boolean") \
     macro(null, null, "null") \
     macro(symbol, symbol, "symbol") \
+    macro(defineDataPropertyIntrinsic, defineDataPropertyIntrinsic, "_DefineDataProperty") \
 
 #endif /* vm_CommonPropertyNames_h */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -570,49 +570,45 @@ intrinsic_DecompileArg(JSContext* cx, un
     return true;
 }
 
 static bool
 intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    MOZ_ASSERT(args.length() >= 3);
+    // When DefineDataProperty is called with 3 arguments, it's compiled to
+    // JSOP_INITELEM in the bytecode emitter so we shouldn't get here.
+    MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(args[0].isObject());
 
     RootedObject obj(cx, &args[0].toObject());
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[1], &id))
         return false;
     RootedValue value(cx, args[2]);
 
     unsigned attrs = 0;
-    if (args.length() >= 4) {
-        unsigned attributes = args[3].toInt32();
-
-        MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
-                   "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
-        if (attributes & ATTR_ENUMERABLE)
-            attrs |= JSPROP_ENUMERATE;
-
-        MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
-                   "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
-                   "ATTR_NONCONFIGURABLE");
-        if (attributes & ATTR_NONCONFIGURABLE)
-            attrs |= JSPROP_PERMANENT;
-
-        MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
-                   "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
-        if (attributes & ATTR_NONWRITABLE)
-            attrs |= JSPROP_READONLY;
-    } else {
-        // If the fourth argument is unspecified, the attributes are for a
-        // plain data property.
-        attrs = JSPROP_ENUMERATE;
-    }
+    unsigned attributes = args[3].toInt32();
+
+    MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
+               "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
+    if (attributes & ATTR_ENUMERABLE)
+        attrs |= JSPROP_ENUMERATE;
+
+    MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
+               "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
+               "ATTR_NONCONFIGURABLE");
+    if (attributes & ATTR_NONCONFIGURABLE)
+        attrs |= JSPROP_PERMANENT;
+
+    MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
+               "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
+    if (attributes & ATTR_NONWRITABLE)
+        attrs |= JSPROP_READONLY;
 
     Rooted<PropertyDescriptor> desc(cx);
     desc.setDataDescriptor(value, attrs);
     if (!DefineProperty(cx, obj, id, desc))
         return false;
 
     args.rval().setUndefined();
     return true;
@@ -2401,23 +2397,22 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("_NameForTypedArray",      intrinsic_NameForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("AddContentTelemetry",     intrinsic_AddContentTelemetry,     2,0),
+    JS_FN("_DefineDataProperty",     intrinsic_DefineDataProperty,      4,0),
 
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
     JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel,       3,0,
                     IntrinsicSubstringKernel),
-    JS_INLINABLE_FN("_DefineDataProperty",              intrinsic_DefineDataProperty,      4,0,
-                    IntrinsicDefineDataProperty),
     JS_INLINABLE_FN("ObjectHasPrototype",               intrinsic_ObjectHasPrototype,      2,0,
                     IntrinsicObjectHasPrototype),
     JS_INLINABLE_FN("UnsafeSetReservedSlot",            intrinsic_UnsafeSetReservedSlot,   3,0,
                     IntrinsicUnsafeSetReservedSlot),
     JS_INLINABLE_FN("UnsafeGetReservedSlot",            intrinsic_UnsafeGetReservedSlot,   2,0,
                     IntrinsicUnsafeGetReservedSlot),
     JS_INLINABLE_FN("UnsafeGetObjectFromReservedSlot",  intrinsic_UnsafeGetObjectFromReservedSlot, 2,0,
                     IntrinsicUnsafeGetObjectFromReservedSlot),