зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1148661 - Optimize accesses to unboxed expandos in Ion, r=jandem.
This commit is contained in:
Родитель
d7cb635d09
Коммит
6e34197b33
|
@ -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);
|
||||
|
|
218
js/src/jit/MIR.h
218
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<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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче