Bug 1148661 - Optimize accesses to unboxed expandos in Ion, r=jandem.

This commit is contained in:
Brian Hackett 2015-04-07 05:42:44 -06:00
Родитель d7cb635d09
Коммит 6e34197b33
19 изменённых файлов: 785 добавлений и 615 удалений

Просмотреть файл

@ -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<UnboxedPlainObject>()) {
group = obj->group();
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
shape = expando->lastProperty();
} else if (obj->is<TypedObject>()) {
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<UnboxedPlainObject>()) {
// Both the group and shape need to be guarded for unboxed plain objects.
return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
}
if (obj->is<TypedObject>()) {
// 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<UnboxedPlainObject>() || !obj->is<TypedObject>())
if (obj == holder)
return false;
if (!obj->is<UnboxedPlainObject>() && !obj->is<TypedObject>())
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<NativeObject>().lastProperty();
if (obj->is<UnboxedPlainObject>()) {
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().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<NativeObject>().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<NativeObject>().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<NativeObject>(),
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<NativeObject>(),
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<NativeObject>(), 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<UnboxedPlainObject>()) {
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().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<NativeObject>(), 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<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub");
MOZ_ASSERT(obj->as<NativeObject>().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<UnboxedPlainObject>()) {
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().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<UnboxedPlainObject>()) {
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<UnboxedPlainObject>()) {
// 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<PlainObject>()) {
// 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<UnboxedPlainObject>()) {
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<NativeObject>().lastProperty());
RootedShape shape(cx, LastPropertyForSetProp(obj_));
ICSetProp_Native* stub = ICStub::New<ICSetProp_Native>(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)
{ }

Просмотреть файл

@ -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<UnboxedPlainObject>()) {
group = obj->group();
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
shape = expando->lastProperty();
} else if (obj->is<TypedObject>()) {
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<UnboxedPlainObject>()) {
// Both the group and shape need to be guarded for unboxed objects.
return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
}
if (obj->is<TypedObject>()) {
// 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<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16);
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(isFixedSlot_) << 16) |
(static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17);
}
bool generateStubCode(MacroAssembler& masm);
@ -4909,8 +4883,10 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
protected:
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16) |
(static_cast<int32_t>(protoChainDepth_) << 20);
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(isFixedSlot_) << 16) |
(static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 17) |
(static_cast<int32_t>(protoChainDepth_) << 18);
}
bool generateStubCode(MacroAssembler& masm);
@ -4933,7 +4909,11 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
if (newGroup == oldGroup_)
newGroup = nullptr;
RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
RootedShape newShape(cx);
if (obj_->isNative())
newShape = obj_->as<NativeObject>().lastProperty();
else
newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);

Просмотреть файл

@ -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<ICGetPropCallGetter*>(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<ICSetPropCallSetter*>(stub);
if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard()))
if (!AddReceiver(nstub->guard(), receivers, convertUnboxedGroups))
return false;
if (!*holder) {

Просмотреть файл

@ -92,11 +92,9 @@ class BaselineInspector
bool dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond);
public:
typedef Vector<Shape*, 4, JitAllocPolicy> ShapeVector;
typedef Vector<ReceiverGuard::StackGuard, 4, JitAllocPolicy> ReceiverVector;
typedef Vector<ObjectGroup*, 4, JitAllocPolicy> 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);
};

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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>
{

Просмотреть файл

@ -208,6 +208,8 @@
_(GuardObjectGroup) \
_(GuardObjectIdentity) \
_(GuardClass) \
_(GuardUnboxedExpando) \
_(LoadUnboxedExpando) \
_(TypeBarrierV) \
_(TypeBarrierO) \
_(MonitorTypes) \

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<Entry, 4, JitAllocPolicy> nativeShapes_;
Vector<ObjectGroup*, 4, JitAllocPolicy> unboxedGroups_;
Vector<Entry, 4, JitAllocPolicy> 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<SingleObjectPolicy, NoFloatPolicy<1> >::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<Entry, 4, JitAllocPolicy> nativeShapes_;
Vector<ObjectGroup*, 4, JitAllocPolicy> unboxedGroups_;
Vector<Entry, 4, JitAllocPolicy> 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<Shape*, 4, JitAllocPolicy> shapes_;
Vector<ObjectGroup*, 4, JitAllocPolicy> unboxedGroups_;
Vector<ReceiverGuard::StackGuard, 4, JitAllocPolicy> 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<ObjectGroup*> 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,

Просмотреть файл

@ -166,6 +166,8 @@ namespace jit {
_(GuardObjectGroup) \
_(GuardObjectIdentity) \
_(GuardClass) \
_(GuardUnboxedExpando) \
_(LoadUnboxedExpando) \
_(ArrayLength) \
_(SetArrayLength) \
_(TypedArrayLength) \

Просмотреть файл

@ -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()));

Просмотреть файл

@ -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

Просмотреть файл

@ -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 =

Просмотреть файл

@ -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)
{