Bug 1388388 - Add a megamorphic SetElement stub. r=evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 10 Aug 2017 11:12:51 +0200
changeset 373961 4d85d37d083cd5614fe1cb1fd6029cfffc0db2d8
parent 373960 973ca5df0887528178b758e0525937ba3e048555
child 373962 6c606f27cd0a377bb76b134923e73c5633eef065
push id32311
push userkwierso@gmail.com
push dateFri, 11 Aug 2017 01:14:57 +0000
treeherdermozilla-central@253a8560dc34 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1388388
milestone57.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 1388388 - Add a megamorphic SetElement stub. r=evilpie
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2002,16 +2002,52 @@ BaselineCacheIRCompiler::emitCallProxySe
     if (!callVM(masm, ProxySetPropertyByValueInfo))
         return false;
 
     stubFrame.leave(masm);
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitMegamorphicSetElement()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+    bool strict = reader.readBool();
+
+    allocator.discardStack(masm);
+
+    // We need a scratch register but we don't have any registers available on
+    // x86, so temporarily store |obj| in the frame's scratch slot.
+    int scratchOffset = BaselineFrame::reverseOffsetOfScratchValue();
+    masm.storePtr(obj, Address(BaselineFrameReg, scratchOffset));
+
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, obj);
+
+    // Restore |obj|. Because we entered a stub frame we first have to load
+    // the original frame pointer.
+    masm.loadPtr(Address(BaselineFrameReg, 0), obj);
+    masm.loadPtr(Address(obj, scratchOffset), obj);
+
+    masm.Push(Imm32(strict));
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(val);
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    if (!callVM(masm, SetObjectElementInfo))
+        return false;
+
+    stubFrame.leave(masm);
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitTypeMonitorResult()
 {
     allocator.discardStack(masm);
     EmitEnterTypeMonitorIC(masm);
     return true;
 }
 
 bool
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2354,21 +2354,16 @@ BaselineCompiler::emit_JSOP_SETELEM()
 }
 
 bool
 BaselineCompiler::emit_JSOP_STRICTSETELEM()
 {
     return emit_JSOP_SETELEM();
 }
 
-typedef bool (*SetObjectElementFn)(JSContext*, HandleObject, HandleValue,
-                                  HandleValue, HandleValue, bool);
-static const VMFunction SetObjectElementInfo =
-    FunctionInfo<SetObjectElementFn>(js::SetObjectElement, "SetObjectElement");
-
 bool
 BaselineCompiler::emit_JSOP_SETELEM_SUPER()
 {
     bool strict = IsCheckStrictOp(JSOp(*pc));
 
     // Incoming stack is |propval, receiver, obj, rval|. We need to shuffle
     // stack to leave rval when operation is complete.
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2641,16 +2641,20 @@ SetPropIRGenerator::tryAttachStub()
     }
 
     if (lhsVal_.isObject()) {
         RootedObject obj(cx_, &lhsVal_.toObject());
         if (obj->watched())
             return false;
 
         ObjOperandId objId = writer.guardIsObject(objValId);
+        if (IsPropertySetOp(JSOp(*pc_))) {
+            if (tryAttachMegamorphicSetElement(obj, objId, rhsValId))
+                return true;
+        }
         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 (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
@@ -3569,16 +3573,36 @@ SetPropIRGenerator::tryAttachProxyElemen
     writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
     writer.returnFromIC();
 
     trackAttached("ProxyElement");
     return true;
 }
 
 bool
+SetPropIRGenerator::tryAttachMegamorphicSetElement(HandleObject obj, ObjOperandId objId,
+                                                   ValOperandId rhsId)
+{
+    MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
+
+    if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetElem)
+        return false;
+
+    // The generic proxy stubs are faster.
+    if (obj->is<ProxyObject>())
+        return false;
+
+    writer.megamorphicSetElement(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
+    writer.returnFromIC();
+
+    trackAttached("MegamorphicSetElement");
+    return true;
+}
+
+bool
 SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id,
                                          ValOperandId rhsId)
 {
     // Attach a stub when the receiver is a WindowProxy and we can do the set
     // on the Window (the global object).
 
     if (!IsWindowProxy(obj))
         return false;
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -198,16 +198,17 @@ extern const char* CacheKindNames[];
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
                                           \
     _(MegamorphicLoadSlotResult)          \
     _(MegamorphicLoadSlotByValueResult)   \
     _(MegamorphicStoreSlot)               \
+    _(MegamorphicSetElement)              \
     _(MegamorphicHasOwnResult)            \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
@@ -875,16 +876,22 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     void megamorphicStoreSlot(ObjOperandId obj, PropertyName* name, ValOperandId rhs,
                               bool needsTypeBarrier) {
         writeOpWithOperandId(CacheOp::MegamorphicStoreSlot, obj);
         addStubField(uintptr_t(name), StubField::Type::String);
         writeOperandId(rhs);
         buffer_.writeByte(needsTypeBarrier);
     }
+    void megamorphicSetElement(ObjOperandId obj, ValOperandId id, ValOperandId rhs, bool strict) {
+        writeOpWithOperandId(CacheOp::MegamorphicSetElement, obj);
+        writeOperandId(id);
+        writeOperandId(rhs);
+        buffer_.writeByte(uint32_t(strict));
+    }
     void megamorphicHasOwnResult(ObjOperandId obj, ValOperandId id) {
         writeOpWithOperandId(CacheOp::MegamorphicHasOwnResult, obj);
         writeOperandId(id);
     }
 
     void loadBooleanResult(bool val) {
         writeOp(CacheOp::LoadBooleanResult);
         buffer_.writeByte(uint32_t(val));
@@ -1419,16 +1426,17 @@ class MOZ_RAII SetPropIRGenerator : publ
     bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
                                    ValOperandId rhsId);
     bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
                                      ValOperandId rhsId);
     bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
                                   ValOperandId rhsId);
     bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId);
     bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId);
+    bool tryAttachMegamorphicSetElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId);
 
     void trackAttached(const char* name);
 
   public:
     SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                        ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue lhsVal,
                        HandleValue idVal, HandleValue rhsVal, bool needsTypeBarrier = true,
                        bool maybeHasExtraIndexedProps = true);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10206,28 +10206,25 @@ CodeGenerator::visitCallGetElement(LCall
     if (op == JSOP_GETELEM) {
         callVM(GetElementInfo, lir);
     } else {
         MOZ_ASSERT(op == JSOP_CALLELEM);
         callVM(CallElementInfo, lir);
     }
 }
 
-typedef bool (*SetObjectElementFn)(JSContext*, HandleObject, HandleValue, HandleValue,
-                                   bool strict);
-static const VMFunction SetObjectElementInfo =
-    FunctionInfo<SetObjectElementFn>(SetObjectElement, "SetObjectElement");
-
 void
 CodeGenerator::visitCallSetElement(LCallSetElement* lir)
 {
+    Register obj = ToRegister(lir->getOperand(0));
     pushArg(Imm32(lir->mir()->strict()));
+    pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
     pushArg(ToValue(lir, LCallSetElement::Value));
     pushArg(ToValue(lir, LCallSetElement::Index));
-    pushArg(ToRegister(lir->getOperand(0)));
+    pushArg(obj);
     callVM(SetObjectElementInfo, lir);
 }
 
 typedef bool (*InitElementArrayFn)(JSContext*, jsbytecode*, HandleObject, uint32_t, HandleValue);
 static const VMFunction InitElementArrayInfo =
     FunctionInfo<InitElementArrayFn>(js::InitElementArray, "InitElementArray");
 
 void
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -2062,16 +2062,38 @@ IonCacheIRCompiler::emitCallProxySetByVa
     masm.Push(val);
     masm.Push(idVal);
     masm.Push(obj);
 
     return callVM(masm, ProxySetPropertyByValueInfo);
 }
 
 bool
+IonCacheIRCompiler::emitMegamorphicSetElement()
+{
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ConstantOrRegister idVal = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
+    bool strict = reader.readBool();
+
+    allocator.discardStack(masm);
+    prepareVMCall(masm);
+
+    masm.Push(Imm32(strict));
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+    masm.Push(val);
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    return callVM(masm, SetObjectElementInfo);
+}
+
+bool
 IonCacheIRCompiler::emitLoadTypedObjectResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegister scratch2(allocator, masm);
 
     TypedThingLayout layout = reader.typedThingLayout();
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1815,10 +1815,15 @@ GetPrototypeOf(JSContext* cx, HandleObje
 
     RootedObject proto(cx);
     if (!GetPrototype(cx, target, &proto))
         return false;
     rval.setObjectOrNull(proto);
     return true;
 }
 
+typedef bool (*SetObjectElementFn)(JSContext*, HandleObject, HandleValue,
+                                   HandleValue, HandleValue, bool);
+const VMFunction SetObjectElementInfo =
+    FunctionInfo<SetObjectElementFn>(js::SetObjectElement, "SetObjectElement");
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -887,12 +887,14 @@ bool
 ObjectHasGetterSetter(JSContext* cx, JSObject* obj, Shape* propShape);
 
 JSString*
 TypeOfObject(JSObject* obj, JSRuntime* rt);
 
 bool
 GetPrototypeOf(JSContext* cx, HandleObject target, MutableHandleValue rval);
 
+extern const VMFunction SetObjectElementInfo;
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */