diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index e831ab649ba0..ffccbf943f43 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -174,6 +174,48 @@ ReceiverGuard::trace(JSTracer* trc) TraceEdge(trc, &group_, "receiver_guard_group"); } +ReceiverGuard::StackGuard::StackGuard(JSObject* obj) + : group(nullptr), shape(nullptr) +{ + if (obj) { + if (obj->is()) { + group = obj->group(); + if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) + shape = expando->lastProperty(); + } else if (obj->is()) { + group = obj->group(); + } else { + shape = obj->maybeShape(); + } + } +} + +ReceiverGuard::StackGuard::StackGuard(ObjectGroup* group, Shape* shape) + : group(group), shape(shape) +{ + if (group) { + if (IsTypedObjectClass(group->clasp())) + this->shape = nullptr; + else if (group->clasp() != &UnboxedPlainObject::class_) + this->group = nullptr; + } +} + +/* static */ int32_t +ReceiverGuard::keyBits(JSObject* obj) +{ + if (obj->is()) { + // Both the group and shape need to be guarded for unboxed plain objects. + return obj->as().maybeExpando() ? 0 : 1; + } + if (obj->is()) { + // Only the group needs to be guarded for typed objects. + return 2; + } + // Other objects only need the shape to be guarded. + return 3; +} + /* static */ void ICStub::trace(JSTracer* trc) { @@ -3166,14 +3208,13 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm) // GetElem_Fallback // -static void GetFixedOrDynamicSlotOffset(NativeObject* obj, uint32_t slot, - bool* isFixed, uint32_t* offset) +static void GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset) { MOZ_ASSERT(isFixed); MOZ_ASSERT(offset); - *isFixed = obj->isFixedSlot(slot); - *offset = *isFixed ? NativeObject::getFixedSlotOffset(slot) - : obj->dynamicSlotIndex(slot) * sizeof(Value); + *isFixed = shape->slot() < shape->numFixedSlots(); + *offset = *isFixed ? NativeObject::getFixedSlotOffset(shape->slot()) + : (shape->slot() - shape->numFixedSlots()) * sizeof(Value); } static JSObject* @@ -3352,7 +3393,9 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false) MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj)); if (!isDOMProxy && !obj->isNative()) { - if (obj == holder || !obj->is() || !obj->is()) + if (obj == holder) + return false; + if (!obj->is() && !obj->is()) return false; } @@ -3428,45 +3471,54 @@ IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* sh return true; } -static bool -IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, JSObject* holder, Shape* shape) +static Shape* +LastPropertyForSetProp(JSObject* obj) { - if (!shape) - return false; + if (obj->isNative()) + return obj->as().lastProperty(); + if (obj->is()) { + UnboxedExpandoObject* expando = obj->as().maybeExpando(); + return expando ? expando->lastProperty() : nullptr; + } + + return nullptr; +} + +static bool +IsCacheableSetPropWriteSlot(JSObject* obj, Shape* oldShape, Shape* propertyShape) +{ // Object shape must not have changed during the property set. - if (!obj->isNative() || obj->as().lastProperty() != oldShape) + if (LastPropertyForSetProp(obj) != oldShape) return false; - // Currently we only optimize direct writes. - if (obj != holder) - return false; - - if (!shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable()) + if (!propertyShape->hasSlot() || + !propertyShape->hasDefaultSetter() || + !propertyShape->writable()) + { return false; + } return true; } static bool IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape, - jsid id, JSObject* holder, Shape* shape, - size_t* protoChainDepth) + jsid id, Shape* propertyShape, size_t* protoChainDepth) { - if (!shape) + // The property must be the last added property of the object. + if (LastPropertyForSetProp(obj) != propertyShape) return false; - // Property must be set directly on object, and be last added property of object. - if (!obj->isNative() || obj != holder || shape != obj->as().lastProperty()) - return false; - - // Object must be extensible, oldShape must be immediate parent of curShape. - if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape) + // Object must be extensible, oldShape must be immediate parent of current shape. + if (!obj->nonProxyIsExtensible() || propertyShape->previous() != oldShape) return false; // Basic shape checks. - if (shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter() || - !shape->writable()) + if (propertyShape->inDictionary() || + !propertyShape->hasSlot() || + !propertyShape->hasDefaultSetter() || + !propertyShape->writable()) { return false; } @@ -3476,8 +3528,8 @@ IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape, return false; size_t chainDepth = 0; - // walk up the object prototype chain and ensure that all prototypes - // are native, and that all prototypes have setter defined on the property + // Walk up the object prototype chain and ensure that all prototypes are + // native, and that all prototypes have no setter defined on the property. for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) { chainDepth++; // if prototype is non-native, don't optimize @@ -3498,7 +3550,7 @@ IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape, // Only add a IC entry if the dynamic slots didn't change when the shapes // changed. Need to ensure that a shape change for a subsequent object // won't involve reallocating the slot array. - if (NativeObject::dynamicSlotsCount(shape) != NativeObject::dynamicSlotsCount(oldShape)) + if (NativeObject::dynamicSlotsCount(propertyShape) != NativeObject::dynamicSlotsCount(oldShape)) return false; *protoChainDepth = chainDepth; @@ -3758,8 +3810,7 @@ TryAttachNativeGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(&holder->as(), - shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot @@ -5940,7 +5991,7 @@ TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc, } else { bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(current, shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); if (!IsCacheableGetPropReadSlot(global, current, shape)) return true; @@ -6063,8 +6114,7 @@ TryAttachScopeNameStub(JSContext* cx, HandleScript script, ICGetName_Fallback* s bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(&scopeChain->as(), - shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); ICStub* newStub; @@ -6521,8 +6571,10 @@ UpdateExistingGenerationalDOMProxyStub(ICGetProp_Fallback* stub, return false; } +// Return whether obj is in some PreliminaryObjectArray and has a structure +// that might change in the future. static bool -HasUnanalyzedNewScript(JSObject* obj) +IsPreliminaryObject(JSObject* obj) { if (obj->isSingleton()) return false; @@ -6531,6 +6583,9 @@ HasUnanalyzedNewScript(JSObject* obj) if (newScript && !newScript->analyzed()) return true; + if (obj->group()->maybePreliminaryObjects()) + return true; + return false; } @@ -6583,7 +6638,7 @@ TryAttachNativeGetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* if (IsCacheableGetPropReadSlot(obj, holder, shape)) { bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(&holder->as(), shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); // Instantiate this property for singleton holders, for use during Ion compilation. if (IsIonEnabled(cx)) @@ -6600,7 +6655,7 @@ TryAttachNativeGetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* if (!newStub) return false; - if (HasUnanalyzedNewScript(obj)) + if (IsPreliminaryObject(obj)) newStub->notePreliminaryObject(); else StripPreliminaryObjectStubs(cx, stub); @@ -6838,7 +6893,7 @@ TryAttachUnboxedExpandoGetPropStub(JSContext* cx, HandleScript script, jsbytecod bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); @@ -6935,7 +6990,7 @@ TryAttachPrimitiveGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(proto, shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); @@ -8171,16 +8226,33 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC { MOZ_ASSERT(!*attached); - if (!obj->isNative() || obj->watched()) + if (obj->watched()) return true; RootedShape shape(cx); RootedObject holder(cx); if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape)) return false; + if (obj != holder) + return true; + + if (!obj->isNative()) { + if (obj->is()) { + UnboxedExpandoObject* expando = obj->as().maybeExpando(); + if (expando) { + shape = expando->lookup(cx, name); + if (!shape) + return true; + } else { + return true; + } + } else { + return true; + } + } size_t chainDepth; - if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, holder, shape, &chainDepth)) { + if (IsCacheableSetPropAddSlot(cx, obj, oldShape, id, shape, &chainDepth)) { // Don't attach if proto chain depth is too high. if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH) return true; @@ -8196,7 +8268,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(&obj->as(), shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.ADD) stub"); ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup, @@ -8212,7 +8284,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC return true; } - if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) { + if (IsCacheableSetPropWriteSlot(obj, oldShape, shape)) { // For some property writes, such as the initial overwrite of global // properties, TI will not mark the property as having been // overwritten. Don't attach a stub in this case, so that we don't @@ -8225,10 +8297,10 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC bool isFixedSlot; uint32_t offset; - GetFixedOrDynamicSlotOffset(&obj->as(), shape->slot(), &isFixedSlot, &offset); + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub"); - MOZ_ASSERT(obj->as().lastProperty() == oldShape, + MOZ_ASSERT(LastPropertyForSetProp(obj) == oldShape, "Should this really be a SetPropWriteSlot?"); ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset); ICSetProp_Native* newStub = compiler.getStub(compiler.getStubSpace(script)); @@ -8237,7 +8309,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) return false; - if (HasUnanalyzedNewScript(obj)) + if (IsPreliminaryObject(obj)) newStub->notePreliminaryObject(); else StripPreliminaryObjectStubs(cx, stub); @@ -8444,6 +8516,11 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ return false; ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj)); + if (obj->is()) { + if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) + oldShape = expando->lastProperty(); + } + bool attached = false; // There are some reasons we can fail to attach a stub that are temporary. // We want to avoid calling noteUnoptimizableAccess() if the reason we @@ -8581,6 +8658,34 @@ ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle< return true; } +static void +GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj, + Register object, Register scratch, + size_t offsetOfGroup, size_t offsetOfShape, Label* failure) +{ + // Guard against object group. + masm.loadPtr(Address(BaselineStubReg, offsetOfGroup), scratch); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, + failure); + + // Guard against shape or expando shape. + masm.loadPtr(Address(BaselineStubReg, offsetOfShape), scratch); + if (obj->is()) { + Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); + masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); + Label done; + masm.push(object); + masm.loadPtr(expandoAddress, object); + masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); + masm.pop(object); + masm.jump(failure); + masm.bind(&done); + masm.pop(object); + } else { + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); + } +} + bool ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) { @@ -8588,19 +8693,15 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) // Guard input is an object. masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register objReg = masm.extractObject(R0, ExtractTemp0); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratch = regs.takeAny(); - // Unbox and shape guard. - Register objReg = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - - // Guard that the object group matches. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_Native::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch, - &failure); + GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch, + ICSetProp_Native::offsetOfGroup(), + ICSetProp_Native::offsetOfShape(), + &failure); // Stow both R0 and R1 (object and value). EmitStowICValues(masm, 2); @@ -8619,7 +8720,13 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) regs.takeUnchecked(objReg); Register holderReg; - if (isFixedSlot_) { + if (obj_->is()) { + // We are loading off the expando object, so use that for the holder. + holderReg = regs.takeAny(); + masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); + if (!isFixedSlot_) + masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); + } else if (isFixedSlot_) { holderReg = objReg; } else { holderReg = regs.takeAny(); @@ -8684,19 +8791,15 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) // Guard input is an object. masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register objReg = masm.extractObject(R0, ExtractTemp0); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratch = regs.takeAny(); - // Unbox and guard against old shape. - Register objReg = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(0)), scratch); - masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - - // Guard that the object group matches. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch, - &failure); + GuardGroupAndShapeMaybeUnboxedExpando(masm, obj_, objReg, scratch, + ICSetProp_NativeAdd::offsetOfGroup(), + ICSetProp_NativeAddImpl<0>::offsetOfShape(0), + &failure); // Stow both R0 and R1 (object and value). EmitStowICValues(masm, 2); @@ -8728,44 +8831,61 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) regs = availableGeneralRegs(2); scratch = regs.takeAny(); - // Changing object shape. Write the object's new shape. - Address shapeAddr(objReg, JSObject::offsetOfShape()); - EmitPreBarrier(masm, shapeAddr, MIRType_Shape); - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); - masm.storePtr(scratch, shapeAddr); + if (obj_->is()) { + // Try to change the object's group. + Label noGroupChange; - // Try to change the object's group. - Label noGroupChange; + // Check if the cache has a new group to change to. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); + masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange); - // Check if the cache has a new group to change to. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); - masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange); + // Check if the old group still has a newScript. + masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::Equal, + Address(scratch, ObjectGroup::offsetOfAddendum()), + ImmWord(0), + &noGroupChange); - // Check if the old group still has a newScript. - masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::Equal, - Address(scratch, ObjectGroup::offsetOfAddendum()), - ImmWord(0), - &noGroupChange); + // Reload the new group from the cache. + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); - // Reload the new group from the cache. - masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); + // Change the object's group. + Address groupAddr(objReg, JSObject::offsetOfGroup()); + EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup); + masm.storePtr(scratch, groupAddr); - // Change the object's group. - Address groupAddr(objReg, JSObject::offsetOfGroup()); - EmitPreBarrier(masm, groupAddr, MIRType_ObjectGroup); - masm.storePtr(scratch, groupAddr); - - masm.bind(&noGroupChange); + masm.bind(&noGroupChange); + } Register holderReg; regs.add(R0); regs.takeUnchecked(objReg); - if (isFixedSlot_) { - holderReg = objReg; - } else { + + if (obj_->is()) { holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); + masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); + + // Write the expando object's new shape. + Address shapeAddr(holderReg, JSObject::offsetOfShape()); + EmitPreBarrier(masm, shapeAddr, MIRType_Shape); + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); + masm.storePtr(scratch, shapeAddr); + + if (!isFixedSlot_) + masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); + } else { + // Write the object's new shape. + Address shapeAddr(objReg, JSObject::offsetOfShape()); + EmitPreBarrier(masm, shapeAddr, MIRType_Shape); + masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); + masm.storePtr(scratch, shapeAddr); + + if (isFixedSlot_) { + holderReg = objReg; + } else { + holderReg = regs.takeAny(); + masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); + } } // Perform the store. No write barrier required since this is a new @@ -12082,7 +12202,7 @@ ICSetProp_Native::Compiler::getStub(ICStubSpace* space) if (!group) return nullptr; - RootedShape shape(cx, obj_->as().lastProperty()); + RootedShape shape(cx, LastPropertyForSetProp(obj_)); ICSetProp_Native* stub = ICStub::New(space, getStubCode(), group, shape, offset_); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; @@ -12268,7 +12388,7 @@ ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitC Shape* holderShape, JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(shape), + : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(nullptr, shape), holder, holderShape, getter, pcOffset), expandoShape_(expandoShape) { } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 7b25391a39b5..13ed43c41281 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -3873,6 +3873,10 @@ class ReceiverGuard ObjectGroup* group; Shape* shape; + StackGuard() + : group(nullptr), shape(nullptr) + {} + MOZ_IMPLICIT StackGuard(const ReceiverGuard& guard) : group(guard.group_), shape(guard.shape_) {} @@ -3881,31 +3885,15 @@ class ReceiverGuard : group(guard.group), shape(guard.shape) {} - explicit StackGuard(JSObject* obj) - : group(nullptr), shape(nullptr) - { - if (obj) { - if (obj->is()) { - group = obj->group(); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - shape = expando->lastProperty(); - } else if (obj->is()) { - group = obj->group(); - } else { - shape = obj->maybeShape(); - } - } + explicit StackGuard(JSObject* obj); + StackGuard(ObjectGroup* group, Shape* shape); + + bool operator ==(const StackGuard& other) const { + return group == other.group && shape == other.shape; } - explicit StackGuard(Shape* shape) - : group(nullptr), shape(shape) - {} - - Shape* ownShape() const { - // Get a shape belonging to the object itself, rather than an unboxed expando. - if (!group || !group->maybeUnboxedLayout()) - return shape; - return nullptr; + bool operator !=(const StackGuard& other) const { + return !(*this == other); } }; @@ -3931,10 +3919,6 @@ class ReceiverGuard return group_; } - Shape* ownShape() const { - return StackGuard(*this).ownShape(); - } - static size_t offsetOfShape() { return offsetof(ReceiverGuard, shape_); } @@ -3943,20 +3927,8 @@ class ReceiverGuard } // Bits to munge into IC compiler keys when that IC has a ReceiverGuard. - // This uses at two bits for data. - static int32_t keyBits(JSObject* obj) { - if (obj->is()) { - // Both the group and shape need to be guarded for unboxed objects. - return obj->as().maybeExpando() ? 0 : 1; - } - if (obj->is()) { - // Only the group needs to be guarded for typed objects. - return 2; - } - // Other objects only need the shape to be guarded, except for ICs - // which always guard the group. - return 3; - } + // This uses at most two bits for data. + static int32_t keyBits(JSObject* obj); }; // Base class for native GetProp stubs. @@ -4807,7 +4779,9 @@ class ICSetProp_Native : public ICUpdatedStub protected: virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isFixedSlot_) << 16); + return static_cast(kind) | + (static_cast(isFixedSlot_) << 16) | + (static_cast(obj_->is()) << 17); } bool generateStubCode(MacroAssembler& masm); @@ -4909,8 +4883,10 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler protected: virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(isFixedSlot_) << 16) | - (static_cast(protoChainDepth_) << 20); + return static_cast(kind) | + (static_cast(isFixedSlot_) << 16) | + (static_cast(obj_->is()) << 17) | + (static_cast(protoChainDepth_) << 18); } bool generateStubCode(MacroAssembler& masm); @@ -4933,7 +4909,11 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler if (newGroup == oldGroup_) newGroup = nullptr; - RootedShape newShape(cx, obj_->as().lastProperty()); + RootedShape newShape(cx); + if (obj_->isNative()) + newShape = obj_->as().lastProperty(); + else + newShape = obj_->as().maybeExpando()->lastProperty(); return ICStub::New>( space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index c3209cebdab3..f52bc92d2698 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -90,19 +90,28 @@ VectorAppendNoDuplicate(S& list, T value) return list.append(value); } +static bool +AddReceiver(const ReceiverGuard::StackGuard& receiver, + BaselineInspector::ReceiverVector& receivers, + BaselineInspector::ObjectGroupVector& convertUnboxedGroups) +{ + if (receiver.group && receiver.group->maybeUnboxedLayout()) { + if (receiver.group->unboxedLayout().nativeGroup()) + return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group); + } + return VectorAppendNoDuplicate(receivers, receiver); +} + bool -BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, - ShapeVector& nativeShapes, - ObjectGroupVector& unboxedGroups, +BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { - // Return lists of native shapes and unboxed objects seen by the baseline - // IC for the current op. Empty lists indicate no shapes/types are known, - // or there was an uncacheable access. convertUnboxedGroups is used for - // unboxed object groups which have been seen, but have had instances - // converted to native objects and should be eagerly converted by Ion. - MOZ_ASSERT(nativeShapes.empty()); - MOZ_ASSERT(unboxedGroups.empty()); + // Return a list of the receivers seen by the baseline IC for the current + // op. Empty lists indicate no receivers are known, or there was an + // uncacheable access. convertUnboxedGroups is used for unboxed object + // groups which have been seen, but have had instances converted to native + // objects and should be eagerly converted by Ion. + MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); if (!hasBaselineScript()) @@ -113,59 +122,38 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ICStub* stub = entry.firstStub(); while (stub->next()) { - Shape* shape = nullptr; - ObjectGroup* group = nullptr; + ReceiverGuard::StackGuard receiver; if (stub->isGetProp_Native()) { - shape = stub->toGetProp_Native()->receiverGuard().ownShape(); + receiver = stub->toGetProp_Native()->receiverGuard(); } else if (stub->isSetProp_Native()) { - shape = stub->toSetProp_Native()->shape(); + receiver = ReceiverGuard::StackGuard(stub->toSetProp_Native()->group(), + stub->toSetProp_Native()->shape()); } else if (stub->isGetProp_Unboxed()) { - group = stub->toGetProp_Unboxed()->group(); + receiver = ReceiverGuard::StackGuard(stub->toGetProp_Unboxed()->group(), nullptr); } else if (stub->isSetProp_Unboxed()) { - group = stub->toSetProp_Unboxed()->group(); - } - - if (!shape && !group) { - nativeShapes.clear(); - unboxedGroups.clear(); + receiver = ReceiverGuard::StackGuard(stub->toSetProp_Unboxed()->group(), nullptr); + } else { + receivers.clear(); return true; } - if (group && group->unboxedLayout().nativeGroup()) { - if (!VectorAppendNoDuplicate(convertUnboxedGroups, group)) - return false; - shape = group->unboxedLayout().nativeShape(); - group = nullptr; - } - - if (shape) { - if (!VectorAppendNoDuplicate(nativeShapes, shape)) - return false; - } else { - if (!VectorAppendNoDuplicate(unboxedGroups, group)) - return false; - } + if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + return false; stub = stub->next(); } if (stub->isGetProp_Fallback()) { - if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) { - nativeShapes.clear(); - unboxedGroups.clear(); - } + if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) + receivers.clear(); } else { - if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) { - nativeShapes.clear(); - unboxedGroups.clear(); - } + if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) + receivers.clear(); } - // Don't inline if there are more than 5 shapes/groups. - if (nativeShapes.length() + unboxedGroups.length() > 5) { - nativeShapes.clear(); - unboxedGroups.clear(); - } + // Don't inline if there are more than 5 receivers. + if (receivers.length() > 5) + receivers.clear(); return true; } @@ -589,45 +577,18 @@ GlobalShapeForGetPropFunction(ICStub* stub) return nullptr; } -static bool -AddReceiver(BaselineInspector::ShapeVector& nativeShapes, - BaselineInspector::ObjectGroupVector& unboxedGroups, - ReceiverGuard::StackGuard receiver) -{ - if (Shape* shape = receiver.ownShape()) - return VectorAppendNoDuplicate(nativeShapes, shape); - - // Only unboxed objects with no expandos are handled by the common - // getprop/setprop optimizations. - if (!receiver.shape) - return VectorAppendNoDuplicate(unboxedGroups, receiver.group); - - return false; -} - -static bool -AddReceiverForGetPropFunction(BaselineInspector::ShapeVector& nativeShapes, - BaselineInspector::ObjectGroupVector& unboxedGroups, - ICGetPropCallGetter* stub) -{ - if (stub->isOwnGetter()) - return true; - - return AddReceiver(nativeShapes, unboxedGroups, stub->receiverGuard()); -} - bool BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, - ShapeVector& nativeShapes, - ObjectGroupVector& unboxedGroups) + ReceiverVector& receivers, + ObjectGroupVector& convertUnboxedGroups) { if (!hasBaselineScript()) return false; - MOZ_ASSERT(nativeShapes.empty()); - MOZ_ASSERT(unboxedGroups.empty()); + MOZ_ASSERT(receivers.empty()); + MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -638,7 +599,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap { ICGetPropCallGetter* nstub = static_cast(stub); bool isOwn = nstub->isOwnGetter(); - if (!AddReceiverForGetPropFunction(nativeShapes, unboxedGroups, nstub)) + if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) return false; if (!*holder) { @@ -670,21 +631,21 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap if (!*holder) return false; - MOZ_ASSERT(*isOwnProperty == (nativeShapes.empty() && unboxedGroups.empty())); + MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty())); return true; } bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ShapeVector& nativeShapes, - ObjectGroupVector& unboxedGroups) + ReceiverVector& receivers, + ObjectGroupVector& convertUnboxedGroups) { if (!hasBaselineScript()) return false; - MOZ_ASSERT(nativeShapes.empty()); - MOZ_ASSERT(unboxedGroups.empty()); + MOZ_ASSERT(receivers.empty()); + MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -692,7 +653,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter* nstub = static_cast(stub); - if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard())) + if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups)) return false; if (!*holder) { diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 463b5416a33a..7146a43a1eb1 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -92,11 +92,9 @@ class BaselineInspector bool dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond); public: - typedef Vector ShapeVector; + typedef Vector ReceiverVector; typedef Vector ObjectGroupVector; - bool maybeInfoForPropertyOp(jsbytecode* pc, - ShapeVector& nativeShapes, - ObjectGroupVector& unboxedGroups, + bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups); SetElemICInspector setElemICInspector(jsbytecode* pc) { @@ -126,10 +124,10 @@ class BaselineInspector bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, - ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups); + ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups); bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ShapeVector& nativeShapes, ObjectGroupVector& unboxedGroups); + ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups); bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject); }; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 3784e775437d..c62ccec06032 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2261,69 +2261,70 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir) masm.storeValue(value, Address(base, offset)); } +static void +GuardReceiver(MacroAssembler& masm, const ReceiverGuard::StackGuard& guard, + Register obj, Register scratch, Label* miss, bool checkNullExpando) +{ + if (guard.group) { + masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss); + + Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando()); + if (guard.shape) { + masm.loadPtr(expandoAddress, scratch); + masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss); + masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss); + } else if (checkNullExpando) { + masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss); + } + } else { + masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss); + } +} + void CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch, const TypedOrValueRegister& output) { MGetPropertyPolymorphic* mir = ins->mirRaw()->toGetPropertyPolymorphic(); - size_t total = mir->numUnboxedGroups() + mir->numShapes(); - MOZ_ASSERT(total > 1); - - bool groupInScratch = mir->numUnboxedGroups() > 1; - bool shapeInScratch = mir->numShapes() > 1; - Label done; - for (size_t i = 0; i < total; i++) { - bool unboxedGroup = i < mir->numUnboxedGroups(); - - ImmGCPtr comparePtr = unboxedGroup - ? ImmGCPtr(mir->unboxedGroup(i)) - : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups())); - Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape()); - - if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch)) - masm.loadPtr(addr, scratch); - - bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch; + for (size_t i = 0; i < mir->numReceivers(); i++) { + ReceiverGuard::StackGuard receiver = mir->receiver(i); Label next; - if (i == total - 1) { - if (inScratch) - bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot()); - else - bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot()); - } else { - if (inScratch) - masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next); - else - masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next); - } + GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); - if (unboxedGroup) { - const UnboxedLayout::Property* property = - mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name()); - Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); + if (receiver.shape) { + // If this is an unboxed expando access, GuardReceiver loaded the + // expando object into scratch. + Register target = receiver.group ? scratch : obj; - masm.loadUnboxedProperty(propertyAddr, property->type, output); - } else { - Shape* shape = mir->shape(i - mir->numUnboxedGroups()); + Shape* shape = mir->shape(i); if (shape->slot() < shape->numFixedSlots()) { // Fixed slot. - masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())), + masm.loadTypedOrValue(Address(target, NativeObject::getFixedSlotOffset(shape->slot())), output); } else { // Dynamic slot. uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value); - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); + masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch); masm.loadTypedOrValue(Address(scratch, offset), output); } + } else { + const UnboxedLayout::Property* property = + receiver.group->unboxedLayout().lookup(mir->name()); + Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); + + masm.loadUnboxedProperty(propertyAddr, property->type, output); } - if (i != total - 1) + if (i == mir->numReceivers() - 1) { + bailoutFrom(&next, ins->snapshot()); + } else { masm.jump(&done); - masm.bind(&next); + masm.bind(&next); + } } masm.bind(&done); @@ -2354,42 +2355,36 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis { MSetPropertyPolymorphic* mir = ins->mirRaw()->toSetPropertyPolymorphic(); - size_t total = mir->numUnboxedGroups() + mir->numShapes(); - MOZ_ASSERT(total > 1); - - bool groupInScratch = mir->numUnboxedGroups() > 1; - bool shapeInScratch = mir->numShapes() > 1; - Label done; - for (size_t i = 0; i < total; i++) { - bool unboxedGroup = i < mir->numUnboxedGroups(); - - ImmGCPtr comparePtr = unboxedGroup - ? ImmGCPtr(mir->unboxedGroup(i)) - : ImmGCPtr(mir->objShape(i - mir->numUnboxedGroups())); - Address addr(obj, unboxedGroup ? JSObject::offsetOfGroup() : JSObject::offsetOfShape()); - - if ((i == 0 && groupInScratch) || (i == mir->numUnboxedGroups() && shapeInScratch)) - masm.loadPtr(addr, scratch); - - bool inScratch = unboxedGroup ? groupInScratch : shapeInScratch; + for (size_t i = 0; i < mir->numReceivers(); i++) { + ReceiverGuard::StackGuard receiver = mir->receiver(i); Label next; - if (i == total - 1) { - if (inScratch) - bailoutCmpPtr(Assembler::NotEqual, scratch, comparePtr, ins->snapshot()); - else - bailoutCmpPtr(Assembler::NotEqual, addr, comparePtr, ins->snapshot()); - } else { - if (inScratch) - masm.branchPtr(Assembler::NotEqual, scratch, comparePtr, &next); - else - masm.branchPtr(Assembler::NotEqual, addr, comparePtr, &next); - } + GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); - if (unboxedGroup) { + if (receiver.shape) { + // If this is an unboxed expando access, GuardReceiver loaded the + // expando object into scratch. + Register target = receiver.group ? scratch : obj; + + Shape* shape = mir->shape(i); + if (shape->slot() < shape->numFixedSlots()) { + // Fixed slot. + Address addr(target, NativeObject::getFixedSlotOffset(shape->slot())); + if (mir->needsBarrier()) + emitPreBarrier(addr); + masm.storeConstantOrRegister(value, addr); + } else { + // Dynamic slot. + masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch); + Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value)); + if (mir->needsBarrier()) + emitPreBarrier(addr); + masm.storeConstantOrRegister(value, addr); + } + } else { const UnboxedLayout::Property* property = - mir->unboxedGroup(i)->unboxedLayout().lookup(mir->name()); + receiver.group->unboxedLayout().lookup(mir->name()); Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); if (property->type == JSVAL_TYPE_OBJECT) @@ -2400,27 +2395,14 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type)); masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); - } else { - Shape* shape = mir->shape(i - mir->numUnboxedGroups()); - if (shape->slot() < shape->numFixedSlots()) { - // Fixed slot. - Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot())); - if (mir->needsBarrier()) - emitPreBarrier(addr); - masm.storeConstantOrRegister(value, addr); - } else { - // Dynamic slot. - masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch); - Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value)); - if (mir->needsBarrier()) - emitPreBarrier(addr); - masm.storeConstantOrRegister(value, addr); - } } - if (i != total - 1) + if (i == mir->numReceivers() - 1) { + bailoutFrom(&next, ins->snapshot()); + } else { masm.jump(&done); - masm.bind(&next); + masm.bind(&next); + } } masm.bind(&done); @@ -2548,41 +2530,46 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) Register obj = ToRegister(lir->object()); Register temp = ToRegister(lir->temp()); - MOZ_ASSERT(mir->numShapes() + mir->numUnboxedGroups() > 1); - Label done; - if (mir->numShapes()) { - masm.loadObjShape(obj, temp); + for (size_t i = 0; i < mir->numReceivers(); i++) { + const ReceiverGuard::StackGuard& receiver = mir->receiver(i); - for (size_t i = 0; i < mir->numShapes(); i++) { - Shape* shape = mir->getShape(i); - if (i == mir->numShapes() - 1 && !mir->numUnboxedGroups()) - bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), lir->snapshot()); - else - masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done); - } - } + Label next; + GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true); - if (mir->numUnboxedGroups()) { - // The guard requires that unboxed objects not have expandos. - bailoutCmpPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfShape()), - ImmWord(0), lir->snapshot()); - - masm.loadObjGroup(obj, temp); - - for (size_t i = 0; i < mir->numUnboxedGroups(); i++) { - ObjectGroup* group = mir->getUnboxedGroup(i); - if (i == mir->numUnboxedGroups() - 1) - bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(group), lir->snapshot()); - else - masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(group), &done); + if (i == mir->numReceivers() - 1) { + bailoutFrom(&next, lir->snapshot()); + } else { + masm.jump(&done); + masm.bind(&next); } } masm.bind(&done); } +void +CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir) +{ + Label miss; + + Register obj = ToRegister(lir->object()); + masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual, + Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss); + + bailoutFrom(&miss, lir->snapshot()); +} + +void +CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir) +{ + Register obj = ToRegister(lir->object()); + Register result = ToRegister(lir->getDef(0)); + + masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result); +} + void CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 78621fa088de..20bbcc8f88e2 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -123,6 +123,8 @@ class CodeGenerator : public CodeGeneratorSpecific void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir); void visitGuardObjectIdentity(LGuardObjectIdentity* guard); void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir); + void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir); + void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir); void visitTypeBarrierV(LTypeBarrierV* lir); void visitTypeBarrierO(LTypeBarrierO* lir); void visitMonitorTypes(LMonitorTypes* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 30833206e80d..3f8a9a9f0884 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -10593,22 +10593,24 @@ IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* nam MDefinition* IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, - const BaselineInspector::ShapeVector& receiverShapes, - const BaselineInspector::ObjectGroupVector& receiverUnboxedGroups, + const BaselineInspector::ReceiverVector& receivers, + const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty) { MOZ_ASSERT(holder); MOZ_ASSERT(holderShape); + obj = convertUnboxedObjects(obj, convertUnboxedGroups); + if (isOwnProperty) { - MOZ_ASSERT(receiverShapes.empty()); + MOZ_ASSERT(receivers.empty()); return addShapeGuard(obj, holderShape, Bailout_ShapeGuard); } MDefinition* holderDef = constantMaybeNursery(holder); addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard); - return addGuardReceiverPolymorphic(obj, receiverShapes, receiverUnboxedGroups); + return addGuardReceiverPolymorphic(obj, receivers); } bool @@ -10622,11 +10624,11 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName Shape* globalShape = nullptr; JSObject* foundProto = nullptr; bool isOwnProperty = false; - BaselineInspector::ShapeVector receiverShapes(alloc()); - BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc()); + BaselineInspector::ReceiverVector receivers(alloc()); + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter, &globalShape, &isOwnProperty, - receiverShapes, receiverUnboxedGroups)) + receivers, convertUnboxedGroups)) { return true; } @@ -10642,7 +10644,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName // If type information is bad, we can still optimize the getter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receiverShapes, receiverUnboxedGroups, + receivers, convertUnboxedGroups, isOwnProperty); if (!obj) return false; @@ -10754,20 +10756,19 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName } bool -IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ShapeVector& nativeShapes, - const BaselineInspector::ObjectGroupVector& unboxedGroups) +IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers) { - if (nativeShapes.empty() && unboxedGroups.empty()) { + if (receivers.empty()) { trackOptimizationOutcome(TrackedOutcome::NoShapeInfo); return false; } - for (size_t i = 0; i < nativeShapes.length(); i++) { + for (size_t i = 0; i < receivers.length(); i++) { // We inline the property access as long as the shape is not in // dictionary mode. We cannot be sure that the shape is still a // lastProperty, and calling Shape::search() on dictionary mode // shapes that aren't lastProperty is invalid. - if (nativeShapes[i]->inDictionary()) { + if (receivers[i].shape && receivers[i].shape->inDictionary()) { trackOptimizationOutcome(TrackedOutcome::InDictionaryMode); return false; } @@ -10776,32 +10777,27 @@ IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ShapeVector& nati return true; } -static bool -GetPropertyShapes(jsid id, const BaselineInspector::ShapeVector& shapes, - BaselineInspector::ShapeVector& propShapes, bool* sameSlot) +static Shape* +PropertyShapesHaveSameSlot(const BaselineInspector::ReceiverVector& receivers, jsid id) { - MOZ_ASSERT(propShapes.empty()); + Shape* firstShape = nullptr; + for (size_t i = 0; i < receivers.length(); i++) { + if (receivers[i].group) + return nullptr; - if (!propShapes.reserve(shapes.length())) - return false; - - *sameSlot = true; - for (size_t i = 0; i < shapes.length(); i++) { - Shape* objShape = shapes[i]; - Shape* shape = objShape->searchLinear(id); + Shape* shape = receivers[i].shape->searchLinear(id); MOZ_ASSERT(shape); - propShapes.infallibleAppend(shape); - if (i > 0) { - if (shape->slot() != propShapes[0]->slot() || - shape->numFixedSlots() != propShapes[0]->numFixedSlots()) - { - *sameSlot = false; - } + if (i == 0) { + firstShape = shape; + } else if (shape->slot() != firstShape->slot() || + shape->numFixedSlots() != firstShape->numFixedSlots()) + { + return nullptr; } } - return true; + return firstShape; } bool @@ -10815,12 +10811,12 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return true; } - BaselineInspector::ShapeVector nativeShapes(alloc()); - BaselineInspector::ObjectGroupVector unboxedGroups(alloc()), convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups, convertUnboxedGroups)) + BaselineInspector::ReceiverVector receivers(alloc()); + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) return false; - if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups)) + if (!canInlinePropertyOpShapes(receivers)) return true; obj = convertUnboxedObjects(obj, convertUnboxedGroups); @@ -10829,18 +10825,55 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) rvalType = MIRType_Value; - if (nativeShapes.length() == 1 && unboxedGroups.empty()) { - // In the monomorphic case, use separate ShapeGuard and LoadSlot - // instructions. - spew("Inlining monomorphic GETPROP"); + if (receivers.length() == 1) { + if (!receivers[0].group) { + // Monomorphic load from a native object. + spew("Inlining monomorphic native GETPROP"); - Shape* objShape = nativeShapes[0]; - obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard); + obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); - Shape* shape = objShape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); - if (!loadSlot(obj, shape, rvalType, barrier, types)) + if (!loadSlot(obj, shape, rvalType, barrier, types)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + if (receivers[0].shape) { + // Monomorphic load from an unboxed object expando. + spew("Inlining monomorphic unboxed expando GETPROP"); + + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); + + MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); + current->add(expando); + + expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); + + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); + + if (!loadSlot(expando, shape, rvalType, barrier, types)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + // Monomorphic load from an unboxed object. + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + + const UnboxedLayout::Property* property = receivers[0].group->unboxedLayout().lookup(name); + MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); + current->push(load); + + if (!pushTypeBarrier(load, types, barrier)) return false; trackOptimizationOutcome(TrackedOutcome::Monomorphic); @@ -10848,37 +10881,15 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return true; } - if (nativeShapes.empty() && unboxedGroups.length() == 1) { - spew("Inlining monomorphic unboxed GETPROP"); - - ObjectGroup* group = unboxedGroups[0]; - obj = addGroupGuard(obj, group, Bailout_ShapeGuard); - - const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); - MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); - current->push(load); - - if (!pushTypeBarrier(load, types, barrier)) - return false; - - *emitted = true; - return true; - } - - MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1); + MOZ_ASSERT(receivers.length() > 1); spew("Inlining polymorphic GETPROP"); - BaselineInspector::ShapeVector propShapes(alloc()); - bool sameSlot; - if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot)) - return false; - - if (sameSlot && unboxedGroups.empty()) { - obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups); + if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) { + obj = addGuardReceiverPolymorphic(obj, receivers); if (!obj) return false; - if (!loadSlot(obj, propShapes[0], rvalType, barrier, types)) + if (!loadSlot(obj, propShape, rvalType, barrier, types)) return false; trackOptimizationOutcome(TrackedOutcome::Polymorphic); @@ -10890,13 +10901,13 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName current->add(load); current->push(load); - for (size_t i = 0; i < nativeShapes.length(); i++) { - if (!load->addShape(nativeShapes[i], propShapes[i])) - return false; - } - - for (size_t i = 0; i < unboxedGroups.length(); i++) { - if (!load->addUnboxedGroup(unboxedGroups[i])) + for (size_t i = 0; i < receivers.length(); i++) { + Shape* propShape = nullptr; + if (receivers[i].shape) { + propShape = receivers[i].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(propShape); + } + if (!load->addReceiver(receivers[i], propShape)) return false; } @@ -11130,11 +11141,11 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, JSFunction* commonSetter = nullptr; JSObject* foundProto = nullptr; bool isOwnProperty; - BaselineInspector::ShapeVector receiverShapes(alloc()); - BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc()); + BaselineInspector::ReceiverVector receivers(alloc()); + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter, &isOwnProperty, - receiverShapes, receiverUnboxedGroups)) + receivers, convertUnboxedGroups)) { trackOptimizationOutcome(TrackedOutcome::NoProtoFound); return true; @@ -11149,7 +11160,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, // If type information is bad, we can still optimize the setter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receiverShapes, receiverUnboxedGroups, + receivers, convertUnboxedGroups, isOwnProperty); if (!obj) return false; @@ -11498,42 +11509,63 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, return true; } - BaselineInspector::ShapeVector nativeShapes(alloc()); - BaselineInspector::ObjectGroupVector unboxedGroups(alloc()), convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, nativeShapes, unboxedGroups, convertUnboxedGroups)) + BaselineInspector::ReceiverVector receivers(alloc()); + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); + if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) return false; - if (!canInlinePropertyOpShapes(nativeShapes, unboxedGroups)) + if (!canInlinePropertyOpShapes(receivers)) return true; obj = convertUnboxedObjects(obj, convertUnboxedGroups); - if (nativeShapes.length() == 1 && unboxedGroups.empty()) { - spew("Inlining monomorphic SETPROP"); + if (receivers.length() == 1) { + if (!receivers[0].group) { + // Monomorphic store to a native object. + spew("Inlining monomorphic native SETPROP"); - // The Baseline IC was monomorphic, so we inline the property access as - // long as the shape is not in dictionary mode. We cannot be sure - // that the shape is still a lastProperty, and calling Shape::search - // on dictionary mode shapes that aren't lastProperty is invalid. - Shape* objShape = nativeShapes[0]; - obj = addShapeGuard(obj, objShape, Bailout_ShapeGuard); + obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); - Shape* shape = objShape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); - bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); - if (!storeSlot(obj, shape, value, needsBarrier)) - return false; + bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); + if (!storeSlot(obj, shape, value, needsBarrier)) + return false; - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; - return true; - } + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } - if (nativeShapes.empty() && unboxedGroups.length() == 1) { + if (receivers[0].shape) { + // Monomorphic store to an unboxed object expando. + spew("Inlining monomorphic unboxed expando SETPROP"); + + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); + + MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); + current->add(expando); + + expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); + + Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(shape); + + bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); + if (!storeSlot(expando, shape, value, needsBarrier)) + return false; + + trackOptimizationOutcome(TrackedOutcome::Monomorphic); + *emitted = true; + return true; + } + + // Monomorphic store to an unboxed object. spew("Inlining monomorphic unboxed SETPROP"); - ObjectGroup* group = unboxedGroups[0]; + ObjectGroup* group = receivers[0].group; obj = addGroupGuard(obj, group, Bailout_ShapeGuard); const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); @@ -11541,25 +11573,21 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, current->push(value); + trackOptimizationOutcome(TrackedOutcome::Monomorphic); *emitted = true; return true; } - MOZ_ASSERT(nativeShapes.length() + unboxedGroups.length() > 1); + MOZ_ASSERT(receivers.length() > 1); spew("Inlining polymorphic SETPROP"); - BaselineInspector::ShapeVector propShapes(alloc()); - bool sameSlot; - if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot)) - return false; - - if (sameSlot && unboxedGroups.empty()) { - obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups); + if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) { + obj = addGuardReceiverPolymorphic(obj, receivers); if (!obj) return false; bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); - if (!storeSlot(obj, propShapes[0], value, needsBarrier)) + if (!storeSlot(obj, propShape, value, needsBarrier)) return false; trackOptimizationOutcome(TrackedOutcome::Polymorphic); @@ -11571,16 +11599,13 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, current->add(ins); current->push(value); - for (size_t i = 0; i < nativeShapes.length(); i++) { - Shape* objShape = nativeShapes[i]; - Shape* shape = objShape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); - if (!ins->addShape(objShape, shape)) - return false; - } - - for (size_t i = 0; i < unboxedGroups.length(); i++) { - if (!ins->addUnboxedGroup(unboxedGroups[i])) + for (size_t i = 0; i < receivers.length(); i++) { + Shape* propShape = nullptr; + if (receivers[i].shape) { + propShape = receivers[i].shape->searchLinear(NameToId(name)); + MOZ_ASSERT(propShape); + } + if (!ins->addReceiver(receivers[i], propShape)) return false; } @@ -12466,12 +12491,10 @@ IonBuilder::addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bail } MInstruction* -IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind, - bool checkUnboxedExpando) +IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind) { MGuardObjectGroup* guard = MGuardObjectGroup::New(alloc(), obj, group, - /* bailOnEquality = */ false, - bailoutKind, checkUnboxedExpando); + /* bailOnEquality = */ false, bailoutKind); current->add(guard); // If a shape guard failed in the past, don't optimize group guards. @@ -12486,35 +12509,46 @@ IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bail } MInstruction* -IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, - const BaselineInspector::ShapeVector& shapes, - const BaselineInspector::ObjectGroupVector& unboxedGroups) +IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind) { - if (shapes.length() == 1 && unboxedGroups.empty()) - return addShapeGuard(obj, shapes[0], Bailout_ShapeGuard); + MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind); + current->add(guard); - if (shapes.empty() && unboxedGroups.length() == 1) { - // The guard requires that unboxed objects not have expando objects. - // An inline cache will be used in these cases. - return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard, - /* checkUnboxedExpando = */ true); + // If a shape guard failed in the past, don't optimize group guards. + if (failedShapeGuard_) + guard->setNotMovable(); + + return guard; +} + +MInstruction* +IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, + const BaselineInspector::ReceiverVector& receivers) +{ + if (receivers.length() == 1) { + if (!receivers[0].group) { + // Monomorphic guard on a native object. + return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); + } + + if (!receivers[0].shape) { + // Guard on an unboxed object that does not have an expando. + obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); + return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard); + } + + // Monomorphic receiver guards are not yet supported when the receiver + // is an unboxed object with an expando. } - MOZ_ASSERT(shapes.length() + unboxedGroups.length() > 1); - MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj); current->add(guard); if (failedShapeGuard_) guard->setNotMovable(); - for (size_t i = 0; i < shapes.length(); i++) { - if (!guard->addShape(shapes[i])) - return nullptr; - } - - for (size_t i = 0; i < unboxedGroups.length(); i++) { - if (!guard->addUnboxedGroup(unboxedGroups[i])) + for (size_t i = 0; i < receivers.length(); i++) { + if (!guard->addReceiver(receivers[i])) return nullptr; } diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 69e59a2490a8..380bcbc6d7a2 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -401,13 +401,11 @@ class IonBuilder MDefinition* addMaybeCopyElementsForWrite(MDefinition* object); MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length); MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind); - MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind, - bool checkUnboxedExpando = false); + MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind); + MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind); MInstruction* - addGuardReceiverPolymorphic(MDefinition* obj, - const BaselineInspector::ShapeVector& shapes, - const BaselineInspector::ObjectGroupVector& unboxedGroups); + addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers); MDefinition* convertShiftToMaskForStaticTypedArray(MDefinition* id, Scalar::Type viewType); @@ -921,8 +919,8 @@ class IonBuilder MDefinition* addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, - const BaselineInspector::ShapeVector& receiverShapes, - const BaselineInspector::ObjectGroupVector& receiverGroups, + const BaselineInspector::ReceiverVector& receivers, + const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty); bool annotateGetPropertyCache(MDefinition* obj, MGetPropertyCache* getPropCache, @@ -946,8 +944,7 @@ class IonBuilder MDefinition* value); bool freezePropTypeSets(TemporaryTypeSet* types, JSObject* foundProto, PropertyName* name); - bool canInlinePropertyOpShapes(const BaselineInspector::ShapeVector& nativeShapes, - const BaselineInspector::ObjectGroupVector& unboxedGroups); + bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers); TemporaryTypeSet* bytecodeTypes(jsbytecode* pc); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 637f76d7f901..8be42a7a1f35 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -6128,6 +6128,38 @@ class LGuardReceiverPolymorphic : public LInstructionHelper<0, 1, 1> } }; +class LGuardUnboxedExpando : public LInstructionHelper<0, 1, 0> +{ + public: + LIR_HEADER(GuardUnboxedExpando) + + explicit LGuardUnboxedExpando(const LAllocation& in) { + setOperand(0, in); + } + const LAllocation* object() { + return getOperand(0); + } + const MGuardUnboxedExpando* mir() const { + return mir_->toGuardUnboxedExpando(); + } +}; + +class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(LoadUnboxedExpando) + + explicit LLoadUnboxedExpando(const LAllocation& in) { + setOperand(0, in); + } + const LAllocation* object() { + return getOperand(0); + } + const MLoadUnboxedExpando* mir() const { + return mir_->toLoadUnboxedExpando(); + } +}; + // Guard that a value is in a TypeSet. class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 5b37a29f2d4d..82f179c254c9 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -208,6 +208,8 @@ _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ + _(GuardUnboxedExpando) \ + _(LoadUnboxedExpando) \ _(TypeBarrierV) \ _(TypeBarrierO) \ _(MonitorTypes) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 0c8b0d3a33e5..e50bb785234e 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3269,6 +3269,24 @@ LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins) redefine(ins, ins->obj()); } +void +LIRGenerator::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) +{ + LGuardUnboxedExpando* guard = + new(alloc()) LGuardUnboxedExpando(useRegister(ins->obj())); + assignSnapshot(guard, ins->bailoutKind()); + add(guard, ins); + redefine(ins, ins->obj()); +} + +void +LIRGenerator::visitLoadUnboxedExpando(MLoadUnboxedExpando* ins) +{ + LLoadUnboxedExpando* lir = + new(alloc()) LLoadUnboxedExpando(useRegisterAtStart(ins->object())); + define(lir, ins); +} + void LIRGenerator::visitAssertRange(MAssertRange* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b7ab871fb5a0..a8e6243729a9 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -221,6 +221,8 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGuardObject(MGuardObject* ins); void visitGuardString(MGuardString* ins); void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins); + void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins); + void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins); void visitPolyInlineGuard(MPolyInlineGuard* ins); void visitAssertRange(MAssertRange* ins); void visitCallGetProperty(MCallGetProperty* ins); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 556730f4ac86..ae3ee1e48d50 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4342,17 +4342,10 @@ MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const const MGuardReceiverPolymorphic* other = ins->toGuardReceiverPolymorphic(); - if (numShapes() != other->numShapes()) + if (numReceivers() != other->numReceivers()) return false; - for (size_t i = 0; i < numShapes(); i++) { - if (getShape(i) != other->getShape(i)) - return false; - } - - if (numUnboxedGroups() != other->numUnboxedGroups()) - return false; - for (size_t i = 0; i < numUnboxedGroups(); i++) { - if (getUnboxedGroup(i) != other->getUnboxedGroup(i)) + for (size_t i = 0; i < numReceivers(); i++) { + if (receiver(i) != other->receiver(i)) return false; } @@ -4498,8 +4491,10 @@ MGetPropertyPolymorphic::mightAlias(const MDefinition* store) const if (!store->isStoreFixedSlot() && !store->isStoreSlot()) return true; - for (size_t i = 0; i < numShapes(); i++) { + for (size_t i = 0; i < numReceivers(); i++) { const Shape* shape = this->shape(i); + if (!shape) + continue; if (shape->slot() < shape->numFixedSlots()) { // Fixed slot. uint32_t slot = shape->slot(); @@ -5119,8 +5114,7 @@ AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj, if (key->isGroup()) { guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality, - Bailout_ObjectIdentityOrTypeGuard, - /* checkUnboxedExpando = */ false); + Bailout_ObjectIdentityOrTypeGuard); } else { MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton()); current->add(singletonConst); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4a1704158556..e5438f4a416b 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -17,6 +17,7 @@ #include "builtin/SIMD.h" #include "jit/AtomicOp.h" +#include "jit/BaselineIC.h" #include "jit/FixedList.h" #include "jit/InlineList.h" #include "jit/JitAllocPolicy.h" @@ -9755,28 +9756,26 @@ class MGetPropertyCache bool updateForReplacement(MDefinition* ins) override; }; -// Emit code to load a value from an object if its shape/group matches one of -// the shapes/groups observed by the baseline IC, else bails out. +// Emit code to load a value from an object if it matches one of the receivers +// observed by the baseline IC, else bails out. class MGetPropertyPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data { struct Entry { - // The shape to guard against. - Shape* objShape; + // The group and/or shape to guard against. + ReceiverGuard::StackGuard receiver; - // The property to laod. + // The property to load, null for loads from unboxed properties. Shape* shape; }; - Vector nativeShapes_; - Vector unboxedGroups_; + Vector receivers_; AlwaysTenuredPropertyName name_; MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name) : MUnaryInstruction(obj), - nativeShapes_(alloc), - unboxedGroups_(alloc), + receivers_(alloc), name_(name) { setGuard(); @@ -9799,29 +9798,20 @@ class MGetPropertyPolymorphic return congruentIfOperandsEqual(ins); } - bool addShape(Shape* objShape, Shape* shape) { + bool addReceiver(const ReceiverGuard::StackGuard receiver, Shape* shape) { Entry entry; - entry.objShape = objShape; + entry.receiver = receiver; entry.shape = shape; - return nativeShapes_.append(entry); + return receivers_.append(entry); } - bool addUnboxedGroup(ObjectGroup* group) { - return unboxedGroups_.append(group); + size_t numReceivers() const { + return receivers_.length(); } - size_t numShapes() const { - return nativeShapes_.length(); - } - Shape* objShape(size_t i) const { - return nativeShapes_[i].objShape; + const ReceiverGuard::StackGuard receiver(size_t i) const { + return receivers_[i].receiver; } Shape* shape(size_t i) const { - return nativeShapes_[i].shape; - } - size_t numUnboxedGroups() const { - return unboxedGroups_.length(); - } - ObjectGroup* unboxedGroup(size_t i) const { - return unboxedGroups_[i]; + return receivers_[i].shape; } PropertyName* name() const { return name_; @@ -9830,10 +9820,17 @@ class MGetPropertyPolymorphic return getOperand(0); } AliasSet getAliasSet() const override { + bool hasUnboxedLoad = false; + for (size_t i = 0; i < numReceivers(); i++) { + if (!shape(i)) { + hasUnboxedLoad = true; + break; + } + } return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | - (!unboxedGroups_.empty() ? AliasSet::UnboxedElement : 0)); + (hasUnboxedLoad ? AliasSet::UnboxedElement : 0)); } bool mightAlias(const MDefinition* store) const override; @@ -9846,23 +9843,21 @@ class MSetPropertyPolymorphic public MixPolicy >::Data { struct Entry { - // The shape to guard against. - Shape* objShape; + // The group and/or shape to guard against. + ReceiverGuard::StackGuard receiver; - // The property to load. + // The property to store, null for stores to unboxed properties. Shape* shape; }; - Vector nativeShapes_; - Vector unboxedGroups_; + Vector receivers_; AlwaysTenuredPropertyName name_; bool needsBarrier_; MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value, PropertyName* name) : MBinaryInstruction(obj, value), - nativeShapes_(alloc), - unboxedGroups_(alloc), + receivers_(alloc), name_(name), needsBarrier_(false) { @@ -9876,29 +9871,20 @@ class MSetPropertyPolymorphic return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name); } - bool addShape(Shape* objShape, Shape* shape) { + bool addReceiver(const ReceiverGuard::StackGuard& receiver, Shape* shape) { Entry entry; - entry.objShape = objShape; + entry.receiver = receiver; entry.shape = shape; - return nativeShapes_.append(entry); + return receivers_.append(entry); } - bool addUnboxedGroup(ObjectGroup* group) { - return unboxedGroups_.append(group); + size_t numReceivers() const { + return receivers_.length(); } - size_t numShapes() const { - return nativeShapes_.length(); - } - Shape* objShape(size_t i) const { - return nativeShapes_[i].objShape; + const ReceiverGuard::StackGuard& receiver(size_t i) const { + return receivers_[i].receiver; } Shape* shape(size_t i) const { - return nativeShapes_[i].shape; - } - size_t numUnboxedGroups() const { - return unboxedGroups_.length(); - } - ObjectGroup* unboxedGroup(size_t i) const { - return unboxedGroups_[i]; + return receivers_[i].shape; } PropertyName* name() const { return name_; @@ -9916,10 +9902,17 @@ class MSetPropertyPolymorphic needsBarrier_ = true; } AliasSet getAliasSet() const override { + bool hasUnboxedStore = false; + for (size_t i = 0; i < numReceivers(); i++) { + if (!shape(i)) { + hasUnboxedStore = true; + break; + } + } return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot | - (!unboxedGroups_.empty() ? AliasSet::UnboxedElement : 0)); + (hasUnboxedStore ? AliasSet::UnboxedElement : 0)); } }; @@ -10214,13 +10207,11 @@ class MGuardReceiverPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data { - Vector shapes_; - Vector unboxedGroups_; + Vector receivers_; MGuardReceiverPolymorphic(TempAllocator& alloc, MDefinition* obj) : MUnaryInstruction(obj), - shapes_(alloc), - unboxedGroups_(alloc) + receivers_(alloc) { setGuard(); setMovable(); @@ -10238,24 +10229,14 @@ class MGuardReceiverPolymorphic return getOperand(0); } - bool addShape(Shape* shape) { - return shapes_.append(shape); + bool addReceiver(const ReceiverGuard::StackGuard& receiver) { + return receivers_.append(receiver); } - size_t numShapes() const { - return shapes_.length(); + size_t numReceivers() const { + return receivers_.length(); } - Shape* getShape(size_t i) const { - return shapes_[i]; - } - - bool addUnboxedGroup(ObjectGroup* group) { - return unboxedGroups_.append(group); - } - size_t numUnboxedGroups() const { - return unboxedGroups_.length(); - } - ObjectGroup* getUnboxedGroup(size_t i) const { - return unboxedGroups_[i]; + const ReceiverGuard::StackGuard& receiver(size_t i) const { + return receivers_[i]; } bool congruentTo(const MDefinition* ins) const override; @@ -10273,15 +10254,13 @@ class MGuardObjectGroup AlwaysTenured group_; bool bailOnEquality_; BailoutKind bailoutKind_; - bool checkUnboxedExpando_; MGuardObjectGroup(MDefinition* obj, ObjectGroup* group, bool bailOnEquality, - BailoutKind bailoutKind, bool checkUnboxedExpando) + BailoutKind bailoutKind) : MUnaryInstruction(obj), group_(group), bailOnEquality_(bailOnEquality), - bailoutKind_(bailoutKind), - checkUnboxedExpando_(checkUnboxedExpando) + bailoutKind_(bailoutKind) { setGuard(); setMovable(); @@ -10296,10 +10275,8 @@ class MGuardObjectGroup INSTRUCTION_HEADER(GuardObjectGroup) static MGuardObjectGroup* New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group, - bool bailOnEquality, BailoutKind bailoutKind, - bool checkUnboxedExpando) { - return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind, - checkUnboxedExpando); + bool bailOnEquality, BailoutKind bailoutKind) { + return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind); } MDefinition* obj() const { @@ -10314,9 +10291,6 @@ class MGuardObjectGroup BailoutKind bailoutKind() const { return bailoutKind_; } - bool checkUnboxedExpando() const { - return checkUnboxedExpando_; - } bool congruentTo(const MDefinition* ins) const override { if (!ins->isGuardObjectGroup()) return false; @@ -10420,6 +10394,84 @@ class MGuardClass ALLOW_CLONE(MGuardClass) }; +// Guard on the presence or absence of an unboxed object's expando. +class MGuardUnboxedExpando + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + bool requireExpando_; + BailoutKind bailoutKind_; + + MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind) + : MUnaryInstruction(obj), + requireExpando_(requireExpando), + bailoutKind_(bailoutKind) + { + setGuard(); + setMovable(); + setResultType(MIRType_Object); + } + + public: + INSTRUCTION_HEADER(GuardUnboxedExpando) + + static MGuardUnboxedExpando* New(TempAllocator& alloc, MDefinition* obj, + bool requireExpando, BailoutKind bailoutKind) { + return new(alloc) MGuardUnboxedExpando(obj, requireExpando, bailoutKind); + } + + MDefinition* obj() const { + return getOperand(0); + } + bool requireExpando() const { + return requireExpando_; + } + BailoutKind bailoutKind() const { + return bailoutKind_; + } + bool congruentTo(const MDefinition* ins) const override { + if (!congruentIfOperandsEqual(ins)) + return false; + if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando()) + return false; + return true; + } + AliasSet getAliasSet() const override { + return AliasSet::Load(AliasSet::ObjectFields); + } +}; + +// Load an unboxed plain object's expando. +class MLoadUnboxedExpando + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + private: + explicit MLoadUnboxedExpando(MDefinition* object) + : MUnaryInstruction(object) + { + setResultType(MIRType_Object); + setMovable(); + } + + public: + INSTRUCTION_HEADER(LoadUnboxedExpando) + + static MLoadUnboxedExpando* New(TempAllocator& alloc, MDefinition* object) { + return new(alloc) MLoadUnboxedExpando(object); + } + + MDefinition* object() const { + return getOperand(0); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { + return AliasSet::Load(AliasSet::ObjectFields); + } +}; + // Load from vp[slot] (slots that are not inline in an object). class MLoadSlot : public MUnaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 5387dfefdc09..0156544142e4 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -166,6 +166,8 @@ namespace jit { _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ + _(GuardUnboxedExpando) \ + _(LoadUnboxedExpando) \ _(ArrayLength) \ _(SetArrayLength) \ _(TypedArrayLength) \ diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index e2bc4a17e6fb..b15cb76f8782 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1664,12 +1664,6 @@ CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup* guard) Register tmp = ToRegister(guard->tempInt()); MOZ_ASSERT(obj != tmp); - if (guard->mir()->checkUnboxedExpando()) { - masm.ma_ldr(DTRAddr(obj, DtrOffImm(UnboxedPlainObject::offsetOfExpando())), tmp); - masm.ma_cmp(tmp, ImmWord(0)); - bailoutIf(Assembler::NotEqual, guard->snapshot()); - } - masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp); masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group())); diff --git a/js/src/jit/mips/CodeGenerator-mips.cpp b/js/src/jit/mips/CodeGenerator-mips.cpp index 3354885f7640..d722ed528ed5 100644 --- a/js/src/jit/mips/CodeGenerator-mips.cpp +++ b/js/src/jit/mips/CodeGenerator-mips.cpp @@ -1753,11 +1753,6 @@ CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup* guard) Register tmp = ToRegister(guard->tempInt()); MOZ_ASSERT(obj != tmp); - if (guard->mir()->checkUnboxedExpando()) { - masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), tmp); - bailoutCmpPtr(Assembler::NotEqual, tmp, ImmWord(0), guard->snapshot()); - } - masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); Assembler::Condition cond = guard->mir()->bailOnEquality() ? Assembler::Equal diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 234df3aeb6b3..68b8ace830e5 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2071,11 +2071,6 @@ CodeGeneratorX86Shared::visitGuardObjectGroup(LGuardObjectGroup* guard) { Register obj = ToRegister(guard->input()); - if (guard->mir()->checkUnboxedExpando()) { - masm.cmpPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0)); - bailoutIf(Assembler::NotEqual, guard->snapshot()); - } - masm.cmpPtr(Operand(obj, JSObject::offsetOfGroup()), ImmGCPtr(guard->mir()->group())); Assembler::Condition cond = diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 8ad11a7f5b23..191b75725425 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -751,6 +751,11 @@ JSFlatString::isIndexSlow(const char16_t* s, size_t length, uint32_t* indexp); const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) }; #undef R +#undef R2 +#undef R4 +#undef R6 +#undef R7 + bool StaticStrings::init(JSContext* cx) {