Bug 1091978 - Make CacheIR AddProp stub support dynamic slot (re)allocation. r=evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 03 Feb 2017 13:40:11 +0100
changeset 332406 a2caf5127bf42f6a1d1cee7a58b19ea8e234e5e2
parent 332405 d3774f2ebaf2c41d7205d1e2c89728a6bbbc50d4
child 332407 49a12a1d55a3c9cc67ba1462e758922fa490b023
push id86522
push userjandemooij@gmail.com
push dateFri, 03 Feb 2017 12:40:55 +0000
treeherdermozilla-inbound@a2caf5127bf4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1091978
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 1091978 - Make CacheIR AddProp stub support dynamic slot (re)allocation. r=evilpie
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/vm/NativeObject.cpp
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -35,17 +35,17 @@ class MOZ_RAII BaselineCacheIRCompiler :
     bool makesGCCalls_;
 
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
 
     MOZ_MUST_USE bool callTypeUpdateIC(AutoStubFrame& stubFrame, Register obj, ValueOperand val,
                                        Register scratch, LiveGeneralRegisterSet saveRegs);
 
     MOZ_MUST_USE bool emitStoreSlotShared(bool isFixed);
-    MOZ_MUST_USE bool emitAddAndStoreSlotShared(bool isFixed);
+    MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
 
   public:
     friend class AutoStubFrame;
 
     BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, ICStubEngine engine,
                             uint32_t stubDataOffset)
       : CacheIRCompiler(cx, writer, Mode::Baseline),
         engine_(engine),
@@ -768,32 +768,68 @@ BaselineCacheIRCompiler::emitStoreFixedS
 
 bool
 BaselineCacheIRCompiler::emitStoreDynamicSlot()
 {
     return emitStoreSlotShared(false);
 }
 
 bool
-BaselineCacheIRCompiler::emitAddAndStoreSlotShared(bool isFixed)
+BaselineCacheIRCompiler::emitAddAndStoreSlotShared(CacheOp op)
 {
     ObjOperandId objId = reader.objOperandId();
     Address offsetAddr = stubAddress(reader.stubOffset());
 
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
     AutoStubFrame stubFrame(*this);
     AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
     bool changeGroup = reader.readBool();
     Address newGroupAddr = stubAddress(reader.stubOffset());
     Address newShapeAddr = stubAddress(reader.stubOffset());
 
+    if (op == CacheOp::AllocateAndStoreDynamicSlot) {
+        // We have to (re)allocate dynamic slots. Do this first, as it's the
+        // only fallible operation here. This simplifies the callTypeUpdateIC
+        // call below: it does not have to worry about saving registers used by
+        // failure paths.
+        Address numNewSlotsAddr = stubAddress(reader.stubOffset());
+
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+
+        AllocatableRegisterSet regs(RegisterSet::Volatile());
+        LiveRegisterSet save(regs.asLiveSet());
+
+        // Use ICStubReg as second scratch.
+        if (!save.has(ICStubReg))
+            save.add(ICStubReg);
+
+        masm.PushRegsInMask(save);
+
+        masm.setupUnalignedABICall(scratch);
+        masm.loadJSContext(scratch);
+        masm.passABIArg(scratch);
+        masm.passABIArg(obj);
+        masm.load32(numNewSlotsAddr, ICStubReg);
+        masm.passABIArg(ICStubReg);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::growSlotsDontReportOOM));
+        masm.mov(ReturnReg, scratch);
+
+        LiveRegisterSet ignore;
+        ignore.add(scratch);
+        masm.PopRegsInMaskIgnore(save, ignore);
+
+        masm.branchIfFalseBool(scratch, failure->label());
+    }
+
     LiveGeneralRegisterSet saveRegs;
     saveRegs.add(obj);
     saveRegs.add(val);
     if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
         return false;
 
     if (changeGroup) {
         // Changing object's group from a partially to fully initialized group,
@@ -820,43 +856,51 @@ BaselineCacheIRCompiler::emitAddAndStore
     Address shapeAddr(obj, ShapedObject::offsetOfShape());
     masm.loadPtr(newShapeAddr, scratch);
     EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
     masm.storePtr(scratch, shapeAddr);
 
     // Perform the store. No pre-barrier required since this is a new
     // initialization.
     masm.load32(offsetAddr, scratch);
-    if (isFixed) {
+    if (op == CacheOp::AddAndStoreFixedSlot) {
         BaseIndex slot(obj, scratch, TimesOne);
         masm.storeValue(val, slot);
     } else {
+        MOZ_ASSERT(op == CacheOp::AddAndStoreDynamicSlot ||
+                   op == CacheOp::AllocateAndStoreDynamicSlot);
         // To avoid running out of registers on x86, use ICStubReg as scratch.
         // We don't need it anymore.
         Register slots = ICStubReg;
         masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), slots);
         BaseIndex slot(slots, scratch, TimesOne);
         masm.storeValue(val, slot);
     }
 
     if (cx_->nursery().exists())
         BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitAddAndStoreFixedSlot()
 {
-    return emitAddAndStoreSlotShared(true);
+    return emitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);
 }
 
 bool
 BaselineCacheIRCompiler::emitAddAndStoreDynamicSlot()
 {
-    return emitAddAndStoreSlotShared(false);
+    return emitAddAndStoreSlotShared(CacheOp::AddAndStoreDynamicSlot);
+}
+
+bool
+BaselineCacheIRCompiler::emitAllocateAndStoreDynamicSlot()
+{
+    return emitAddAndStoreSlotShared(CacheOp::AllocateAndStoreDynamicSlot);
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreUnboxedProperty()
 {
     ObjOperandId objId = reader.objOperandId();
     JSValueType fieldType = reader.valueType();
     Address offsetAddr = stubAddress(reader.stubOffset());
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1979,20 +1979,16 @@ SetPropIRGenerator::tryAttachAddSlotStub
 
         // Otherwise, if there's no such property, watch out for a resolve hook
         // that would need to be invoked and thus prevent inlining of property
         // addition.
         if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto))
             return false;
     }
 
-    // Only optimize if the dynamic slots didn't change. TODO: bug 1091978.
-    if (NativeObject::dynamicSlotsCount(propShape) != NativeObject::dynamicSlotsCount(oldShape))
-        return false;
-
     // Don't attach if we are adding a property to an object which the new
     // script properties analysis hasn't been performed for yet, as there
     // may be a shape change required here afterwards.
     if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
         *isTemporarilyUnoptimizable_ = true;
         return false;
     }
 
@@ -2036,16 +2032,24 @@ SetPropIRGenerator::tryAttachAddSlotStub
     MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
 
     if (holderOrExpando->isFixedSlot(propShape->slot())) {
         size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
         writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
                                     changeGroup, newGroup);
     } else {
         size_t offset = holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
-        writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
-                                      changeGroup, newGroup);
+        uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
+        uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
+        if (numOldSlots == numNewSlots) {
+            writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
+                                          changeGroup, newGroup);
+        } else {
+            MOZ_ASSERT(numNewSlots > numOldSlots);
+            writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
+                                               changeGroup, newGroup, numNewSlots);
+        }
     }
     writer.returnFromIC();
 
     setUpdateStubInfo(oldGroup, id);
     return true;
 }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -169,16 +169,17 @@ enum class CacheKind : uint8_t
     _(LoadDOMExpandoValueGuardGeneration) \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     _(StoreFixedSlot)                     \
     _(StoreDynamicSlot)                   \
     _(AddAndStoreFixedSlot)               \
     _(AddAndStoreDynamicSlot)             \
+    _(AllocateAndStoreDynamicSlot)        \
     _(StoreTypedObjectReferenceProperty)  \
     _(StoreTypedObjectScalarProperty)     \
     _(StoreUnboxedProperty)               \
     _(CallNativeSetter)                   \
     _(CallScriptedSetter)                 \
     _(CallSetArrayLength)                 \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
@@ -591,16 +592,28 @@ class MOZ_RAII CacheIRWriter : public JS
     {
         writeOpWithOperandId(CacheOp::AddAndStoreDynamicSlot, obj);
         addStubField(offset, StubField::Type::RawWord);
         writeOperandId(rhs);
         buffer_.writeByte(changeGroup);
         addStubField(uintptr_t(newGroup), StubField::Type::ObjectGroup);
         addStubField(uintptr_t(newShape), StubField::Type::Shape);
     }
+    void allocateAndStoreDynamicSlot(ObjOperandId obj, size_t offset, ValOperandId rhs,
+                                     Shape* newShape, bool changeGroup, ObjectGroup* newGroup,
+                                     uint32_t numNewSlots)
+    {
+        writeOpWithOperandId(CacheOp::AllocateAndStoreDynamicSlot, obj);
+        addStubField(offset, StubField::Type::RawWord);
+        writeOperandId(rhs);
+        buffer_.writeByte(changeGroup);
+        addStubField(uintptr_t(newGroup), StubField::Type::ObjectGroup);
+        addStubField(uintptr_t(newShape), StubField::Type::Shape);
+        addStubField(numNewSlots, StubField::Type::RawWord);
+    }
 
     void storeTypedObjectReferenceProperty(ObjOperandId obj, uint32_t offset,
                                            TypedThingLayout layout, ReferenceTypeDescr::Type type,
                                            ValOperandId rhs)
     {
         writeOpWithOperandId(CacheOp::StoreTypedObjectReferenceProperty, obj);
         addStubField(offset, StubField::Type::RawWord);
         buffer_.writeByte(uint32_t(layout));
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -857,16 +857,22 @@ IonCacheIRCompiler::emitAddAndStoreFixed
 
 bool
 IonCacheIRCompiler::emitAddAndStoreDynamicSlot()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
+IonCacheIRCompiler::emitAllocateAndStoreDynamicSlot()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
 IonCacheIRCompiler::emitStoreUnboxedProperty()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectReferenceProperty()
 {
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -465,16 +465,19 @@ NativeObject::growSlots(JSContext* cx, u
     Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCount, newCount - oldCount);
 
     return true;
 }
 
 /* static */ bool
 NativeObject::growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t newCount)
 {
+    // IC code calls this directly.
+    AutoCheckCannotGC nogc;
+
     if (!obj->growSlots(cx, obj->numDynamicSlots(), newCount)) {
         cx->recoverFromOutOfMemory();
         return false;
     }
     return true;
 }
 
 static void