Bug 1137180 - Allow unboxed objects to be extended with new properties, r=jandem.

This commit is contained in:
Brian Hackett 2015-03-15 14:26:42 -06:00
Родитель 394a2dd441
Коммит 94ad027986
21 изменённых файлов: 1008 добавлений и 447 удалений

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

@ -1708,6 +1708,9 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
goto scan_unboxed;
}
if (clasp == &UnboxedPlainObject::class_) {
JSObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (expando && mark(expando))
repush(expando);
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
unboxedTraceList = layout.traceList();
if (!unboxedTraceList)

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

@ -320,7 +320,6 @@ class GCMarker : public JSTracer
void markAndScanString(JSObject *source, JSString *str);
void markAndScanSymbol(JSObject *source, JS::Symbol *sym);
bool markObject(JSObject *source, JSObject *obj);
void appendGrayRoot(void *thing, JSGCTraceKind kind);

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

@ -168,7 +168,6 @@ ICStub::updateCode(JitCode *code)
void
ReceiverGuard::trace(JSTracer *trc)
{
MOZ_ASSERT(!!shape_ != !!group_);
if (shape_)
MarkShape(trc, &shape_, "receiver_guard_shape");
else
@ -347,12 +346,12 @@ ICStub::trace(JSTracer *trc)
}
case ICStub::GetProp_Native: {
ICGetProp_Native *propStub = toGetProp_Native();
MarkShape(trc, &propStub->shape(), "baseline-getpropnative-stub-shape");
propStub->receiverGuard().trace(trc);
break;
}
case ICStub::GetProp_NativePrototype: {
ICGetProp_NativePrototype *propStub = toGetProp_NativePrototype();
propStub->guard().trace(trc);
propStub->receiverGuard().trace(trc);
MarkObject(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder");
MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
break;
@ -426,13 +425,12 @@ ICStub::trace(JSTracer *trc)
}
case ICStub::SetProp_Native: {
ICSetProp_Native *propStub = toSetProp_Native();
MarkShape(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnative-stub-group");
propStub->receiverGuard().trace(trc);
break;
}
case ICStub::SetProp_NativeAdd: {
ICSetProp_NativeAdd *propStub = toSetProp_NativeAdd();
MarkObjectGroup(trc, &propStub->group(), "baseline-setpropnativeadd-stub-group");
propStub->receiverGuard().trace(trc);
MarkShape(trc, &propStub->newShape(), "baseline-setpropnativeadd-stub-newshape");
if (propStub->newGroup())
MarkObjectGroup(trc, &propStub->newGroup(), "baseline-setpropnativeadd-stub-new-group");
@ -3448,16 +3446,22 @@ IsCacheableSetPropWriteSlot(JSObject *obj, Shape *oldShape, JSObject *holder, Sh
}
static bool
IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
HandleId id, HandleObject holder, HandleShape shape,
IsCacheableSetPropAddSlot(JSContext *cx, JSObject *obj, Shape *oldShape,
jsid id, JSObject *holder, Shape *shape,
size_t *protoChainDepth)
{
if (!shape)
if (!shape || obj != holder)
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;
if (obj->isNative()) {
if (shape != obj->as<NativeObject>().lastProperty())
return false;
} else if (obj->is<UnboxedPlainObject>()) {
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando || shape != expando->lastProperty())
return false;
}
// Object must be extensible, oldShape must be immediate parent of curShape.
if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
@ -3497,7 +3501,7 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape 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 (obj->as<NativeObject>().numDynamicSlots() != oldSlots)
if (NativeObject::dynamicSlotsCount(shape) != NativeObject::dynamicSlotsCount(oldShape))
return false;
*protoChainDepth = chainDepth;
@ -5778,7 +5782,7 @@ UpdateExistingGetPropCallStubs(ICFallbackStub *fallbackStub,
bool isOwnGetter = (holder == receiver);
bool foundMatchingStub = false;
ReceiverGuard::Token receiverGuard = ReceiverGuard::objectToken(receiver);
ReceiverGuard::StackGuard receiverGuard(receiver);
for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
if (iter->kind() == kind) {
ICGetPropCallGetter *getPropStub = static_cast<ICGetPropCallGetter *>(*iter);
@ -5804,10 +5808,12 @@ UpdateExistingGetPropCallStubs(ICFallbackStub *fallbackStub,
// matches as protos with the old shape flow into it, but always
// matches post-get, which is where we are now.
getPropStub->holderShape() = holder->lastProperty();
// Make sure to update the getter, since a shape change might
// have changed which getter we want to use.
getPropStub->getter() = getter;
if (receiverGuard == getPropStub->receiverGuard().token())
if (getPropStub->receiverGuard().matches(receiverGuard))
foundMatchingStub = true;
}
}
@ -5822,7 +5828,7 @@ static bool
UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
ICStub::Kind kind,
NativeObject *holder,
ReceiverGuard::Token receiverGuard,
ReceiverGuard::StackGuard receiverGuard,
JSFunction *setter)
{
MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
@ -5835,13 +5841,13 @@ UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
// We want to update the holder shape to match the new one no
// matter what, even if the receiver shape is different.
MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
setPropStub->guard().token() != receiverGuard,
!setPropStub->guard().matches(receiverGuard),
"Why didn't we end up using this stub?");
setPropStub->holderShape() = holder->lastProperty();
// Make sure to update the setter, since a shape change might
// have changed which setter we want to use.
setPropStub->setter() = setter;
if (receiverGuard == setPropStub->guard().token())
if (setPropStub->guard().matches(receiverGuard))
foundMatchingStub = true;
}
}
@ -6712,6 +6718,45 @@ TryAttachUnboxedGetPropStub(JSContext *cx, HandleScript script,
return true;
}
static bool
TryAttachUnboxedExpandoGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
bool *attached)
{
MOZ_ASSERT(!*attached);
if (!val.isObject() || !val.toObject().is<UnboxedPlainObject>())
return true;
Rooted<UnboxedPlainObject *> obj(cx, &val.toObject().as<UnboxedPlainObject>());
Rooted<UnboxedExpandoObject *> expando(cx, obj->maybeExpando());
if (!expando)
return true;
Shape *shape = expando->lookup(cx, name);
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
return true;
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, isCallProp, monitorStub, obj, obj,
name, isFixedSlot, offset);
ICGetPropNativeStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
StripPreliminaryObjectStubs(cx, stub);
stub->addNewStub(newStub);
*attached = true;
return true;
}
static bool
TryAttachTypedObjectGetPropStub(JSContext *cx, HandleScript script,
ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
@ -6967,6 +7012,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
if (attached)
return true;
if (!TryAttachUnboxedExpandoGetPropStub(cx, script, pc, stub, name, val, &attached))
return false;
if (attached)
return true;
if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached))
return false;
if (attached)
@ -7145,16 +7195,16 @@ ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler &masm)
ICGetPropNativeStub *
ICGetPropNativeCompiler::getStub(ICStubSpace *space)
{
ReceiverGuard::StackGuard guard(obj_);
switch (kind) {
case ICStub::GetProp_Native: {
MOZ_ASSERT(obj_ == holder_);
RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, shape, offset_);
return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, guard, offset_);
}
case ICStub::GetProp_NativePrototype: {
MOZ_ASSERT(obj_ != holder_);
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, guard,
offset_, holder_, holderShape);
@ -7166,21 +7216,40 @@ ICGetPropNativeCompiler::getStub(ICStubSpace *space)
}
static void
GuardNativeOrUnboxedReceiver(MacroAssembler &masm, JSObject *obj,
GuardNativeOrUnboxedReceiver(MacroAssembler &masm, ReceiverGuard::StackGuard guard,
Register object, Register scratch,
size_t receiverGuardOffset, Label *failure)
{
if (obj->isNative()) {
masm.loadPtr(Address(BaselineStubReg,
receiverGuardOffset + ReceiverGuard::offsetOfShape()),
scratch);
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
} else {
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
masm.loadPtr(Address(BaselineStubReg,
receiverGuardOffset + ReceiverGuard::offsetOfGroup()),
scratch);
Address groupAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfGroup());
Address shapeAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfShape());
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
if (guard.group) {
masm.loadPtr(groupAddress, scratch);
masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
if (guard.group->maybeUnboxedLayout() && !guard.shape) {
// Guard the unboxed object has no expando object.
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
}
}
if (guard.shape) {
masm.loadPtr(shapeAddress, scratch);
if (guard.group && guard.group->maybeUnboxedLayout()) {
// Guard the unboxed object has a matching expando object.
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);
}
}
}
@ -7204,14 +7273,18 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Shape/group guard.
MOZ_ASSERT(ICGetProp_Native::offsetOfShape() ==
ICGetProp_NativePrototype::offsetOfGuard() + ReceiverGuard::offsetOfShape());
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
ICGetProp_NativePrototype::offsetOfGuard(), &failure);
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
ICGetPropNativeStub::offsetOfReceiverGuard(), &failure);
Register holderReg;
if (obj_ == holder_) {
holderReg = objReg;
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);
} else {
holderReg = objReg;
}
} else {
// Shape guard holder.
holderReg = regs.takeAny();
@ -7348,7 +7421,7 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm)
// Unbox and guard against old shape/group.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure);
Register protoReg = regs.takeAny();
@ -7387,7 +7460,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch,
ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure);
if (receiver_ != holder_) {
@ -7495,7 +7568,7 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Shape guard.
GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch,
ICGetProp_CallNative::offsetOfReceiverGuard(), &failure);
if (receiver_ != holder_ ) {
@ -7985,21 +8058,34 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset)
// value property.
static bool
TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, uint32_t oldSlots,
HandleObject obj, const ReceiverGuard::RootedStackGuard &oldGuard,
HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached)
{
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 (!holder)
return true;
if (holder->is<UnboxedPlainObject>()) {
UnboxedExpandoObject *expando = holder->as<UnboxedPlainObject>().maybeExpando();
if (expando) {
shape = expando->lookup(cx, name);
if (!shape)
return true;
} else {
return true;
}
}
size_t chainDepth;
if (IsCacheableSetPropAddSlot(cx, obj, oldShape, oldSlots, id, holder, shape, &chainDepth)) {
if (IsCacheableSetPropAddSlot(cx, obj, oldGuard.shape, id, holder, shape, &chainDepth)) {
// Don't attach if proto chain depth is too high.
if (chainDepth > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
return true;
@ -8008,18 +8094,22 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
// script properties analysis hasn't been performed for yet, as there
// may be a shape change required here afterwards. Pretend we attached
// a stub, though, so the access is not marked as unoptimizable.
if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
if (oldGuard.group->newScript() && !oldGuard.group->newScript()->analyzed()) {
*attached = true;
return true;
}
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
if (obj->is<NativeObject>()) {
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
} else {
GetFixedOrDynamicSlotOffset(obj->as<UnboxedPlainObject>().maybeExpando(),
shape->slot(), &isFixedSlot, &offset);
}
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.ADD) stub");
ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldGroup,
chainDepth, isFixedSlot, offset);
ICSetPropNativeAddCompiler compiler(cx, obj, oldGuard, chainDepth, isFixedSlot, offset);
ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
@ -8031,7 +8121,10 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
return true;
}
if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) {
if (!obj->isNative())
return true;
if (IsCacheableSetPropWriteSlot(obj, oldGuard.shape, holder, 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
@ -8047,7 +8140,7 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub");
MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldGuard.shape,
"Should this really be a SetPropWriteSlot?");
ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
@ -8072,8 +8165,10 @@ TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, IC
// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
// an accessor property.
static bool
TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
HandleObject obj, ReceiverGuard::Token receiverGuard, HandlePropertyName name,
TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
ICSetProp_Fallback *stub,
HandleObject obj, const ReceiverGuard::RootedStackGuard &receiverGuard,
HandlePropertyName name,
HandleId id, HandleValue rhs, bool *attached,
bool *isTemporarilyUnoptimizable)
{
@ -8179,6 +8274,41 @@ TryAttachUnboxedSetPropStub(JSContext *cx, HandleScript script,
return true;
}
static bool
TryAttachUnboxedExpandoSetPropStub(JSContext *cx, HandleScript script,
ICSetProp_Fallback *stub, HandleId id,
HandleObject obj, Shape *oldShape, HandleValue rhs, bool *attached)
{
MOZ_ASSERT(!*attached);
if (!obj->is<UnboxedPlainObject>())
return true;
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando || expando->lastProperty() != oldShape)
return true;
Shape *shape = expando->lookup(cx, id);
if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
return true;
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(expando, shape->slot(), &isFixedSlot, &offset);
ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub || !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
return false;
stub->addNewStub(newStub);
StripPreliminaryObjectStubs(cx, stub);
*attached = true;
return true;
}
static bool
TryAttachTypedObjectSetPropStub(JSContext *cx, HandleScript script,
ICSetProp_Fallback *stub, HandleId id,
@ -8253,14 +8383,9 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
RootedId id(cx, NameToId(name));
RootedObject obj(cx, ToObjectFromStack(cx, lhs));
if (!obj)
if (!obj || !obj->getGroup(cx))
return false;
ReceiverGuard::Token oldGuard = ReceiverGuard::objectToken(obj);
RootedShape oldShape(cx, obj->maybeShape());
RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
if (!oldGroup)
return false;
uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj, true));
bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
@ -8313,8 +8438,8 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
if (!attached &&
lhs.isObject() &&
!TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape,
oldGroup, oldSlots, name, id, rhs, &attached))
!TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldGuard,
name, id, rhs, &attached))
{
return false;
}
@ -8330,6 +8455,15 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
if (attached)
return true;
if (!attached &&
lhs.isObject() &&
!TryAttachUnboxedExpandoSetPropStub(cx, script, stub, id, obj, oldGuard.shape, rhs, &attached))
{
return false;
}
if (attached)
return true;
if (!attached &&
lhs.isObject() &&
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
@ -8402,23 +8536,17 @@ ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<
bool
ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
Label failure, failurePopObject;
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register objReg = masm.extractObject(R0, ExtractTemp0);
GeneralRegisterSet 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);
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_, true), objReg, scratch,
ICSetProp_Native::offsetOfReceiverGuard(), &failure);
// Stow both R0 and R1 (object and value).
EmitStowICValues(masm, 2);
@ -8437,7 +8565,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();
@ -8462,6 +8596,11 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
masm.moveValue(R1, R0);
EmitReturnFromIC(masm);
if (failurePopObject.used()) {
masm.bind(&failurePopObject);
masm.pop(objReg);
}
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
@ -8472,8 +8611,6 @@ ICUpdatedStub *
ICSetPropNativeAddCompiler::getStub(ICStubSpace *space)
{
AutoShapeVector shapes(cx);
if (!shapes.append(oldShape_))
return nullptr;
if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
return nullptr;
@ -8497,8 +8634,7 @@ ICSetPropNativeAddCompiler::getStub(ICStubSpace *space)
bool
ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
Label failureUnstow;
Label failure, failurePopObject, failureUnstow;
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -8506,15 +8642,10 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratch = regs.takeAny();
// Unbox and guard against old shape.
// Unbox and guard that the object group matches.
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);
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_, true), objReg, scratch,
ICSetProp_NativeAdd::offsetOfReceiverGuard(), &failure);
// Stow both R0 and R1 (object and value).
EmitStowICValues(masm, 2);
@ -8526,7 +8657,7 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
for (size_t i = 0; i < protoChainDepth_; i++) {
masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow);
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<0>::offsetOfShape(i + 1)),
masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAddImpl<1>::offsetOfShape(i)),
scratch);
masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failureUnstow);
}
@ -8546,44 +8677,60 @@ 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
@ -8605,6 +8752,12 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
masm.moveValue(R1, R0);
EmitReturnFromIC(masm);
if (failurePopObject.used()) {
masm.bind(&failurePopObject);
masm.pop(objReg);
masm.jump(&failure);
}
// Failure case - jump to next stub
masm.bind(&failureUnstow);
EmitUnstowICValues(masm, 2);
@ -8823,7 +8976,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
ICSetProp_CallScripted::offsetOfGuard(), &failureUnstow);
Register holderReg = regs.takeAny();
@ -8942,7 +9095,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
GuardNativeOrUnboxedReceiver(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch,
ICSetProp_CallNative::offsetOfGuard(), &failureUnstow);
Register holderReg = regs.takeAny();
@ -11765,8 +11918,10 @@ ICGetProp_Primitive::ICGetProp_Primitive(JitCode *stubCode, ICStub *firstMonitor
{ }
ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode,
ICStub *firstMonitorStub, uint32_t offset)
ICStub *firstMonitorStub,
ReceiverGuard::StackGuard guard, uint32_t offset)
: ICMonitoredStub(kind, stubCode, firstMonitorStub),
receiverGuard_(guard),
offset_(offset)
{ }
@ -11774,15 +11929,14 @@ ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode,
ICGetProp_Native::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
ICGetProp_Native &other)
{
return New<ICGetProp_Native>(space, other.jitCode(), firstMonitorStub, other.shape(),
return New<ICGetProp_Native>(space, other.jitCode(), firstMonitorStub, other.receiverGuard(),
other.offset());
}
ICGetProp_NativePrototype::ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token guard, uint32_t offset,
ReceiverGuard::StackGuard guard, uint32_t offset,
JSObject *holder, Shape *holderShape)
: ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, offset),
guard_(guard),
: ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, guard, offset),
holder_(holder),
holderShape_(holderShape)
{ }
@ -11792,12 +11946,13 @@ ICGetProp_NativePrototype::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
ICGetProp_NativePrototype &other)
{
return New<ICGetProp_NativePrototype>(space, other.jitCode(), firstMonitorStub,
other.guard().token(),
other.offset(), other.holder_, other.holderShape_);
other.receiverGuard(), other.offset(),
other.holder_, other.holderShape_);
}
ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist(
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard, size_t protoChainDepth)
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
size_t protoChainDepth)
: ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub),
guard_(guard)
{
@ -11816,7 +11971,7 @@ ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx)
template <size_t ProtoChainDepth>
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::ICGetProp_NativeDoesNotExistImpl(
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard,
JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
const AutoShapeVector *shapes)
: ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth)
{
@ -11838,7 +11993,7 @@ ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler(
}
ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token receiverGuard, JSObject *holder,
ReceiverGuard::StackGuard receiverGuard, JSObject *holder,
Shape *holderShape, JSFunction *getter,
uint32_t pcOffset)
: ICMonitoredStub(kind, stubCode, firstMonitorStub),
@ -11867,7 +12022,7 @@ ICGetProp_CallScripted::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
ICGetProp_CallScripted &other)
{
return New<ICGetProp_CallScripted>(space, other.jitCode(), firstMonitorStub,
other.receiverGuard().token(),
other.receiverGuard(),
other.holder_, other.holderShape_,
other.getter_, other.pcOffset_);
}
@ -11877,39 +12032,34 @@ ICGetProp_CallNative::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
ICGetProp_CallNative &other)
{
return New<ICGetProp_CallNative>(space, other.jitCode(), firstMonitorStub,
other.receiverGuard().token(), other.holder_,
other.receiverGuard(), other.holder_,
other.holderShape_, other.getter_, other.pcOffset_);
}
ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape,
ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, ReceiverGuard::StackGuard guard,
uint32_t offset)
: ICUpdatedStub(SetProp_Native, stubCode),
group_(group),
shape_(shape),
receiverGuard_(guard),
offset_(offset)
{ }
ICSetProp_Native *
ICSetProp_Native::Compiler::getStub(ICStubSpace *space)
{
RootedObjectGroup group(cx, obj_->getGroup(cx));
if (!group)
return nullptr;
RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
ReceiverGuard::StackGuard guard(obj_, true);
ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), guard, offset_);
if (!stub || !stub->initUpdatingChain(cx, space))
return nullptr;
return stub;
}
ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ReceiverGuard::StackGuard guard,
size_t protoChainDepth,
Shape *newShape,
ObjectGroup *newGroup,
uint32_t offset)
: ICUpdatedStub(SetProp_NativeAdd, stubCode),
group_(group),
receiverGuard_(guard),
newShape_(newShape),
newGroup_(newGroup),
offset_(offset)
@ -11920,28 +12070,26 @@ ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
template <size_t ProtoChainDepth>
ICSetProp_NativeAddImpl<ProtoChainDepth>::ICSetProp_NativeAddImpl(JitCode *stubCode,
ObjectGroup *group,
ReceiverGuard::StackGuard guard,
const AutoShapeVector *shapes,
Shape *newShape,
ObjectGroup *newGroup,
uint32_t offset)
: ICSetProp_NativeAdd(stubCode, group, ProtoChainDepth, newShape, newGroup, offset)
: ICSetProp_NativeAdd(stubCode, guard, ProtoChainDepth, newShape, newGroup, offset)
{
MOZ_ASSERT(shapes->length() == NumShapes);
for (size_t i = 0; i < NumShapes; i++)
for (int i = 0; i < int(NumShapes); i++) // Use an int here to avoid compiler warnings.
shapes_[i].init((*shapes)[i]);
}
ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj,
HandleShape oldShape,
HandleObjectGroup oldGroup,
ReceiverGuard::StackGuard oldGuard,
size_t protoChainDepth,
bool isFixedSlot,
uint32_t offset)
: ICStubCompiler(cx, ICStub::SetProp_NativeAdd),
obj_(cx, obj),
oldShape_(cx, oldShape),
oldGroup_(cx, oldGroup),
oldGuard_(cx, oldGuard),
protoChainDepth_(protoChainDepth),
isFixedSlot_(isFixedSlot),
offset_(offset)
@ -11949,7 +12097,7 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext *cx, HandleObje
MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
}
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard,
ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::StackGuard guard,
JSObject *holder, Shape *holderShape,
JSFunction *setter, uint32_t pcOffset)
: ICStub(kind, stubCode),
@ -11965,14 +12113,14 @@ ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverG
/* static */ ICSetProp_CallScripted *
ICSetProp_CallScripted::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallScripted &other)
{
return New<ICSetProp_CallScripted>(space, other.jitCode(), other.guard().token(), other.holder_,
return New<ICSetProp_CallScripted>(space, other.jitCode(), other.guard(), other.holder_,
other.holderShape_, other.setter_, other.pcOffset_);
}
/* static */ ICSetProp_CallNative *
ICSetProp_CallNative::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallNative &other)
{
return New<ICSetProp_CallNative>(space, other.jitCode(), other.guard().token(), other.holder_,
return New<ICSetProp_CallNative>(space, other.jitCode(), other.guard(), other.holder_,
other.holderShape_, other.setter_, other.pcOffset_);
}
@ -12082,8 +12230,8 @@ ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitC
Shape *holderShape,
JSFunction *getter,
uint32_t pcOffset)
: ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::shapeToken(shape),
holder, holderShape, getter, pcOffset),
: ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(shape),
holder, holderShape, getter, pcOffset),
expandoShape_(expandoShape)
{ }

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

@ -3841,26 +3841,147 @@ class ICGetProp_StringLength : public ICStub
};
};
// Structure encapsulating the guarding that needs to be done on an object
// before it can be accessed or modified.
class ReceiverGuard
{
// Group to guard on, or null. If the object is not unboxed and the IC does
// not require the object to have a specific group, this is null.
// Otherwise, this is the object's group.
HeapPtrObjectGroup group_;
// Shape to guard on, or null. If the object is not unboxed then this is
// the object's shape. If the object is unboxed, then this is the shape of
// the object's expando, null if the object has no expando.
HeapPtrShape shape_;
public:
struct StackGuard;
struct RootedStackGuard
{
RootedObjectGroup group;
RootedShape shape;
RootedStackGuard(JSContext *cx, const StackGuard &guard)
: group(cx, guard.group), shape(cx, guard.shape)
{}
};
struct StackGuard
{
ObjectGroup *group;
Shape *shape;
MOZ_IMPLICIT StackGuard(const ReceiverGuard &guard)
: group(guard.group_), shape(guard.shape_)
{}
MOZ_IMPLICIT StackGuard(const RootedStackGuard &guard)
: group(guard.group), shape(guard.shape)
{}
explicit StackGuard(JSObject *obj, bool guardGroup = false)
: group(nullptr), shape(nullptr)
{
if (obj) {
shape = obj->maybeShape();
if (shape) {
if (guardGroup)
group = obj->group();
} else {
group = obj->group();
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando())
shape = expando->lastProperty();
}
}
}
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;
}
};
explicit ReceiverGuard(const StackGuard &guard)
: group_(guard.group), shape_(guard.shape)
{}
bool matches(const StackGuard &guard) {
return group_ == guard.group && shape_ == guard.shape;
}
void update(const StackGuard &other) {
group_ = other.group;
shape_ = other.shape;
}
void trace(JSTracer *trc);
Shape *shape() const {
return shape_;
}
ObjectGroup *group() const {
return group_;
}
Shape *ownShape() const {
return StackGuard(*this).ownShape();
}
static size_t offsetOfShape() {
return offsetof(ReceiverGuard, shape_);
}
static size_t offsetOfGroup() {
return offsetof(ReceiverGuard, group_);
}
// 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->maybeShape())
return 0;
return obj->as<UnboxedPlainObject>().maybeExpando() ? 1 : 2;
}
};
// Base class for native GetProp stubs.
class ICGetPropNativeStub : public ICMonitoredStub
{
// Object shape/group.
ReceiverGuard receiverGuard_;
// Fixed or dynamic slot offset.
uint32_t offset_;
protected:
ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
uint32_t offset);
ReceiverGuard::StackGuard guard, uint32_t offset);
public:
ReceiverGuard &receiverGuard() {
return receiverGuard_;
}
uint32_t offset() const {
return offset_;
}
void notePreliminaryObject() {
extra_ = 1;
}
bool hasPreliminaryObject() const {
return extra_;
}
static size_t offsetOfReceiverGuard() {
return offsetof(ICGetPropNativeStub, receiverGuard_);
}
static size_t offsetOfOffset() {
return offsetof(ICGetPropNativeStub, offset_);
}
@ -3871,103 +3992,16 @@ class ICGetProp_Native : public ICGetPropNativeStub
{
friend class ICStubSpace;
// Object shape (lastProperty).
HeapPtrShape shape_;
ICGetProp_Native(JitCode *stubCode, ICStub *firstMonitorStub, Shape *shape,
ICGetProp_Native(JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard,
uint32_t offset)
: ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, offset),
shape_(shape)
: ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, guard, offset)
{}
public:
HeapPtrShape &shape() {
return shape_;
}
static size_t offsetOfShape() {
return offsetof(ICGetProp_Native, shape_);
}
static ICGetProp_Native *Clone(ICStubSpace *space, ICStub *firstMonitorStub,
ICGetProp_Native &other);
};
// Structure encapsulating the guarding that needs to be done on an object
// which might be either native or unboxed. In the former case, only the
// object's shape needs to be guarded. In the latter case, only the object's
// group needs to be guarded.
class ReceiverGuard
{
HeapPtrShape shape_;
HeapPtrObjectGroup group_;
public:
typedef uintptr_t Token;
static Token shapeToken(Shape *shape) {
return reinterpret_cast<Token>(shape) | 1;
}
static Token groupToken(ObjectGroup *group) {
return reinterpret_cast<Token>(group);
}
static Shape *tokenShape(Token token) {
if (token & 1)
return reinterpret_cast<Shape *>(token & ~1);
return nullptr;
}
static ObjectGroup *tokenGroup(Token token) {
if (!(token & 1))
return reinterpret_cast<ObjectGroup *>(token);
return nullptr;
}
static Token objectToken(JSObject *obj) {
if (obj->is<UnboxedPlainObject>())
return groupToken(obj->group());
return shapeToken(obj->maybeShape());
}
explicit ReceiverGuard(Token token)
: shape_(tokenShape(token)), group_(tokenGroup(token))
{
MOZ_ASSERT(shape_ || group_);
}
void update(Token token) {
MOZ_ASSERT(!!shape_ == !!tokenShape(token));
MOZ_ASSERT(!!group_ == !!tokenGroup(token));
shape_ = tokenShape(token);
group_ = tokenGroup(token);
MOZ_ASSERT(shape_ || group_);
}
void trace(JSTracer *trc);
Token token() {
MOZ_ASSERT(!!shape_ != !!group_);
if (shape_)
return shapeToken(shape_);
return groupToken(group_);
}
Shape *shape() const {
return shape_;
}
ObjectGroup *group() const {
return group_;
}
static size_t offsetOfShape() {
return offsetof(ReceiverGuard, shape_);
}
static size_t offsetOfGroup() {
return offsetof(ReceiverGuard, group_);
}
};
// Stub for accessing a property on the native prototype of a native or unboxed
// object. Note that due to the shape teleporting optimization, we only have to
// guard on the object's shape/group and the holder's shape.
@ -3976,15 +4010,12 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub
friend class ICStubSpace;
protected:
// Object shape/group.
ReceiverGuard guard_;
// Holder and its shape.
HeapPtrObject holder_;
HeapPtrShape holderShape_;
ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token guard,
ReceiverGuard::StackGuard guard,
uint32_t offset, JSObject *holder, Shape *holderShape);
public:
@ -3993,18 +4024,12 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub
ICGetProp_NativePrototype &other);
public:
ReceiverGuard &guard() {
return guard_;
}
HeapPtrObject &holder() {
return holder_;
}
HeapPtrShape &holderShape() {
return holderShape_;
}
static size_t offsetOfGuard() {
return offsetof(ICGetProp_NativePrototype, guard_);
}
static size_t offsetOfHolder() {
return offsetof(ICGetProp_NativePrototype, holder_);
}
@ -4033,7 +4058,7 @@ class ICGetPropNativeCompiler : public ICStubCompiler
(static_cast<int32_t>(isCallProp_) << 16) |
(static_cast<int32_t>(isFixedSlot_) << 17) |
(static_cast<int32_t>(inputDefinitelyObject_) << 18) |
(static_cast<int32_t>(obj_->isNative()) << 19);
(ReceiverGuard::keyBits(obj_) << 19);
}
public:
@ -4067,7 +4092,7 @@ class ICGetProp_NativeDoesNotExist : public ICMonitoredStub
protected:
ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token guard,
ReceiverGuard::StackGuard guard,
size_t protoChainDepth);
public:
@ -4105,7 +4130,7 @@ class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token guard,
ReceiverGuard::StackGuard guard,
const AutoShapeVector *shapes);
public:
@ -4129,8 +4154,8 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
protected:
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(obj_->isNative()) << 16) |
(static_cast<int32_t>(protoChainDepth_) << 17);
(ReceiverGuard::keyBits(obj_) << 16) |
(static_cast<int32_t>(protoChainDepth_) << 18);
}
bool generateStubCode(MacroAssembler &masm);
@ -4141,8 +4166,9 @@ class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
template <size_t ProtoChainDepth>
ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
ReceiverGuard::StackGuard guard(obj_);
return ICStub::New<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> >
(space, getStubCode(), firstMonitorStub_, ReceiverGuard::objectToken(obj_), shapes);
(space, getStubCode(), firstMonitorStub_, guard, shapes);
}
ICStub *getStub(ICStubSpace *space);
@ -4299,7 +4325,7 @@ class ICGetPropCallGetter : public ICMonitoredStub
uint32_t pcOffset_;
ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token receiverGuard, JSObject *holder,
ReceiverGuard::StackGuard receiverGuard, JSObject *holder,
Shape *holderShape, JSFunction *getter, uint32_t pcOffset);
public:
@ -4351,9 +4377,9 @@ class ICGetPropCallGetter : public ICMonitoredStub
// ICGetProp_CallNative::Compiler::getKey adds more bits to our
// return value, so be careful when making changes here.
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(receiver_->isNative()) << 16) |
(static_cast<int32_t>(!!outerClass_) << 17) |
(static_cast<int32_t>(receiver_ != holder_) << 18);
(ReceiverGuard::keyBits(receiver_) << 16) |
(static_cast<int32_t>(!!outerClass_) << 18) |
(static_cast<int32_t>(receiver_ != holder_) << 19);
}
public:
@ -4382,7 +4408,7 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter
protected:
ICGetProp_CallScripted(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token receiverGuard,
ReceiverGuard::StackGuard receiverGuard,
JSObject *holder, Shape *holderShape,
JSFunction *getter, uint32_t pcOffset)
: ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub,
@ -4406,7 +4432,7 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
ReceiverGuard::StackGuard guard(receiver_);
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
guard, holder_, holderShape, getter_,
@ -4423,7 +4449,8 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
protected:
ICGetProp_CallNative(JitCode *stubCode, ICStub *firstMonitorStub,
ReceiverGuard::Token receiverGuard, JSObject *holder, Shape *holderShape,
ReceiverGuard::StackGuard receiverGuard,
JSObject *holder, Shape *holderShape,
JSFunction *getter, uint32_t pcOffset)
: ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub,
receiverGuard, holder, holderShape, getter, pcOffset)
@ -4441,8 +4468,8 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
virtual int32_t getKey() const {
int32_t baseKey = ICGetPropCallGetter::Compiler::getKey();
MOZ_ASSERT((baseKey >> 19) == 0);
return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 19);
MOZ_ASSERT((baseKey >> 20) == 0);
return baseKey | (static_cast<int32_t>(inputDefinitelyObject_) << 20);
}
public:
@ -4456,7 +4483,7 @@ class ICGetProp_CallNative : public ICGetPropCallGetter
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
ReceiverGuard::StackGuard guard(receiver_);
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_CallNative>(space, getStubCode(), firstMonitorStub_,
guard, holder_, holderShape,
@ -4736,18 +4763,14 @@ class ICSetProp_Native : public ICUpdatedStub
friend class ICStubSpace;
protected: // Protected to silence Clang warning.
HeapPtrObjectGroup group_;
HeapPtrShape shape_;
ReceiverGuard receiverGuard_;
uint32_t offset_;
ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape, uint32_t offset);
ICSetProp_Native(JitCode *stubCode, ReceiverGuard::StackGuard guard, uint32_t offset);
public:
HeapPtrObjectGroup &group() {
return group_;
}
HeapPtrShape &shape() {
return shape_;
ReceiverGuard &receiverGuard() {
return receiverGuard_;
}
void notePreliminaryObject() {
extra_ = 1;
@ -4755,11 +4778,8 @@ class ICSetProp_Native : public ICUpdatedStub
bool hasPreliminaryObject() const {
return extra_;
}
static size_t offsetOfGroup() {
return offsetof(ICSetProp_Native, group_);
}
static size_t offsetOfShape() {
return offsetof(ICSetProp_Native, shape_);
static size_t offsetOfReceiverGuard() {
return offsetof(ICSetProp_Native, receiverGuard_);
}
static size_t offsetOfOffset() {
return offsetof(ICSetProp_Native, offset_);
@ -4772,7 +4792,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) |
(ReceiverGuard::keyBits(obj_) << 17);
}
bool generateStubCode(MacroAssembler &masm);
@ -4798,20 +4820,20 @@ class ICSetProp_NativeAdd : public ICUpdatedStub
static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
protected: // Protected to silence Clang warning.
HeapPtrObjectGroup group_;
ReceiverGuard receiverGuard_;
HeapPtrShape newShape_;
HeapPtrObjectGroup newGroup_;
uint32_t offset_;
ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth,
ICSetProp_NativeAdd(JitCode *stubCode, ReceiverGuard::StackGuard guard, size_t protoChainDepth,
Shape *newShape, ObjectGroup *newGroup, uint32_t offset);
public:
size_t protoChainDepth() const {
return extra_;
}
HeapPtrObjectGroup &group() {
return group_;
ReceiverGuard &receiverGuard() {
return receiverGuard_;
}
HeapPtrShape &newShape() {
return newShape_;
@ -4826,8 +4848,8 @@ class ICSetProp_NativeAdd : public ICUpdatedStub
return static_cast<ICSetProp_NativeAddImpl<ProtoChainDepth> *>(this);
}
static size_t offsetOfGroup() {
return offsetof(ICSetProp_NativeAdd, group_);
static size_t offsetOfReceiverGuard() {
return offsetof(ICSetProp_NativeAdd, receiverGuard_);
}
static size_t offsetOfNewShape() {
return offsetof(ICSetProp_NativeAdd, newShape_);
@ -4845,20 +4867,23 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
{
friend class ICStubSpace;
static const size_t NumShapes = ProtoChainDepth + 1;
static const size_t NumShapes = ProtoChainDepth;
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
ICSetProp_NativeAddImpl(JitCode *stubCode, ObjectGroup *group,
ICSetProp_NativeAddImpl(JitCode *stubCode, ReceiverGuard::StackGuard guard,
const AutoShapeVector *shapes,
Shape *newShape, ObjectGroup *newGroup, uint32_t offset);
public:
void traceShapes(JSTracer *trc) {
for (size_t i = 0; i < NumShapes; i++)
// Note: using int32_t here to avoid gcc warning.
for (int32_t i = 0; i < int32_t(NumShapes); i++)
MarkShape(trc, &shapes_[i], "baseline-setpropnativeadd-stub-shape");
}
static size_t offsetOfShape(size_t idx) {
// The shape array might be aligned differently if its length is zero.
JS_STATIC_ASSERT(NumShapes != 0);
return offsetof(ICSetProp_NativeAddImpl, shapes_) + (idx * sizeof(HeapPtrShape));
}
};
@ -4866,23 +4891,24 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
class ICSetPropNativeAddCompiler : public ICStubCompiler
{
RootedObject obj_;
RootedShape oldShape_;
RootedObjectGroup oldGroup_;
ReceiverGuard::RootedStackGuard oldGuard_;
size_t protoChainDepth_;
bool isFixedSlot_;
uint32_t offset_;
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>(ReceiverGuard::keyBits(obj_)) << 17) |
(static_cast<int32_t>(protoChainDepth_) << 19);
}
bool generateStubCode(MacroAssembler &masm);
public:
ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj,
HandleShape oldShape, HandleObjectGroup oldGroup,
ReceiverGuard::StackGuard oldGuard,
size_t protoChainDepth, bool isFixedSlot, uint32_t offset);
template <size_t ProtoChainDepth>
@ -4895,13 +4921,17 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
// Only specify newGroup when the object's group changes due to the
// object becoming fully initialized per the acquired properties
// analysis.
if (newGroup == oldGroup_)
if (newGroup == oldGuard_.group)
newGroup = nullptr;
RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
RootedShape newShape(cx);
if (obj_->is<UnboxedPlainObject>())
newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
else
newShape = obj_->as<NativeObject>().lastProperty();
return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
space, getStubCode(), oldGuard_, shapes, newShape, newGroup, offset_);
}
ICUpdatedStub *getStub(ICStubSpace *space);
@ -5076,8 +5106,9 @@ class ICSetPropCallSetter : public ICStub
// PC of call, for profiler
uint32_t pcOffset_;
ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
Shape *holderShape, JSFunction *setter, uint32_t pcOffset);
ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::StackGuard guard,
JSObject *holder, Shape *holderShape, JSFunction *setter,
uint32_t pcOffset);
public:
ReceiverGuard &guard() {
@ -5118,7 +5149,7 @@ class ICSetPropCallSetter : public ICStub
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) |
(static_cast<int32_t>(obj_->isNative()) << 16);
(ReceiverGuard::keyBits(obj_) << 16);
}
public:
@ -5141,7 +5172,7 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter
friend class ICStubSpace;
protected:
ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder,
Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
: ICSetPropCallSetter(SetProp_CallScripted, stubCode, guard, holder, holderShape,
setter, pcOffset)
@ -5163,7 +5194,7 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
ReceiverGuard::StackGuard guard(obj_);
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
holderShape, setter_, pcOffset_);
@ -5177,7 +5208,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
friend class ICStubSpace;
protected:
ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder,
Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
: ICSetPropCallSetter(SetProp_CallNative, stubCode, guard, holder, holderShape,
setter, pcOffset)
@ -5199,7 +5230,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
ReceiverGuard::StackGuard guard(obj_);
Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), guard, holder_,
holderShape, setter_, pcOffset_);

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

@ -116,14 +116,16 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc,
Shape *shape = nullptr;
ObjectGroup *group = nullptr;
if (stub->isGetProp_Native()) {
shape = stub->toGetProp_Native()->shape();
shape = stub->toGetProp_Native()->receiverGuard().ownShape();
} else if (stub->isSetProp_Native()) {
shape = stub->toSetProp_Native()->shape();
shape = stub->toSetProp_Native()->receiverGuard().ownShape();
} else if (stub->isGetProp_Unboxed()) {
group = stub->toGetProp_Unboxed()->group();
} else if (stub->isSetProp_Unboxed()) {
group = stub->toSetProp_Unboxed()->group();
} else {
}
if (!shape && !group) {
nativeShapes.clear();
unboxedGroups.clear();
return true;
@ -589,12 +591,17 @@ GlobalShapeForGetPropFunction(ICStub *stub)
static bool
AddReceiver(BaselineInspector::ShapeVector &nativeShapes,
BaselineInspector::ObjectGroupVector &unboxedGroups,
ReceiverGuard::Token receiver)
ReceiverGuard::StackGuard receiver)
{
if (Shape *shape = ReceiverGuard::tokenShape(receiver))
if (Shape *shape = receiver.ownShape())
return VectorAppendNoDuplicate(nativeShapes, shape);
ObjectGroup *group = ReceiverGuard::tokenGroup(receiver);
return VectorAppendNoDuplicate(unboxedGroups, group);
// 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
@ -605,13 +612,7 @@ AddReceiverForGetPropFunction(BaselineInspector::ShapeVector &nativeShapes,
if (stub->isOwnGetter())
return true;
ReceiverGuard::Token token;
if (stub->isGetProp_CallScripted())
token = stub->toGetProp_CallScripted()->receiverGuard().token();
else
token = stub->toGetProp_CallNative()->receiverGuard().token();
return AddReceiver(nativeShapes, unboxedGroups, token);
return AddReceiver(nativeShapes, unboxedGroups, stub->receiverGuard());
}
bool
@ -690,7 +691,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().token()))
if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard()))
return false;
if (!*holder) {

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

@ -2564,6 +2564,10 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic *lir)
}
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++) {

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

@ -10521,7 +10521,7 @@ IonBuilder::addShapeGuardsForGetterSetter(MDefinition *obj, JSObject *holder, Sh
MDefinition *holderDef = constantMaybeNursery(holder);
addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
return addShapeGuardPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
return addGuardReceiverPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
}
bool
@ -10787,7 +10787,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName
return false;
if (sameSlot && unboxedGroups.empty()) {
obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
if (!obj)
return false;
@ -11467,7 +11467,7 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
return false;
if (sameSlot && unboxedGroups.empty()) {
obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
obj = addGuardReceiverPolymorphic(obj, nativeShapes, unboxedGroups);
if (!obj)
return false;
@ -12379,11 +12379,12 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
}
MInstruction *
IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind)
IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind,
bool checkUnboxedExpando)
{
MGuardObjectGroup *guard = MGuardObjectGroup::New(alloc(), obj, group,
/* bailOnEquality = */ false,
bailoutKind);
bailoutKind, checkUnboxedExpando);
current->add(guard);
// If a shape guard failed in the past, don't optimize group guards.
@ -12398,15 +12399,19 @@ IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bail
}
MInstruction *
IonBuilder::addShapeGuardPolymorphic(MDefinition *obj,
const BaselineInspector::ShapeVector &shapes,
const BaselineInspector::ObjectGroupVector &unboxedGroups)
IonBuilder::addGuardReceiverPolymorphic(MDefinition *obj,
const BaselineInspector::ShapeVector &shapes,
const BaselineInspector::ObjectGroupVector &unboxedGroups)
{
if (shapes.length() == 1 && unboxedGroups.empty())
return addShapeGuard(obj, shapes[0], Bailout_ShapeGuard);
if (shapes.empty() && unboxedGroups.length() == 1)
return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard);
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);
}
MOZ_ASSERT(shapes.length() + unboxedGroups.length() > 1);

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

@ -398,12 +398,13 @@ 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);
MInstruction *addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind,
bool checkUnboxedExpando = false);
MInstruction *
addShapeGuardPolymorphic(MDefinition *obj,
const BaselineInspector::ShapeVector &shapes,
const BaselineInspector::ObjectGroupVector &unboxedGroups);
addGuardReceiverPolymorphic(MDefinition *obj,
const BaselineInspector::ShapeVector &shapes,
const BaselineInspector::ObjectGroupVector &unboxedGroups);
MDefinition *convertShiftToMaskForStaticTypedArray(MDefinition *id,
Scalar::Type viewType);

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

@ -699,6 +699,41 @@ IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, Shape *shape)
return true;
}
static void
TestMatchingReceiver(MacroAssembler &masm, IonCache::StubAttacher &attacher,
Register object, JSObject *obj, Label *failure,
bool alwaysCheckGroup = false)
{
if (Shape *shape = obj->maybeShape()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
ImmGCPtr(shape), failure);
if (alwaysCheckGroup)
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
} else {
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
MOZ_ASSERT(failure);
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
Label success;
masm.push(object);
masm.loadPtr(expandoAddress, object);
masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
&success);
masm.pop(object);
masm.jump(failure);
masm.bind(&success);
masm.pop(object);
} else {
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
}
}
}
static inline void
EmitLoadSlot(MacroAssembler &masm, NativeObject *holder, Shape *shape, Register holderReg,
TypedOrValueRegister output, Register scratchReg)
@ -791,14 +826,16 @@ CheckDOMProxyExpandoDoesNotShadow(JSContext *cx, MacroAssembler &masm, JSObject
static void
GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj, NativeObject *holder,
IonCache::StubAttacher &attacher, JSObject *obj, JSObject *holder,
Shape *shape, Register object, TypedOrValueRegister output,
Label *failures = nullptr)
{
// If there's a single jump to |failures|, we can patch the shape guard
// jump directly. Otherwise, jump to the end of the stub, so there's a
// common point to patch.
bool multipleFailureJumps = (obj != holder) || (failures != nullptr && failures->used());
bool multipleFailureJumps = (obj != holder)
|| obj->is<UnboxedPlainObject>()
|| (failures != nullptr && failures->used());
// If we have multiple failure jumps but didn't get a label from the
// outside, make one ourselves.
@ -806,18 +843,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
if (multipleFailureJumps && !failures)
failures = &failures_;
// Guard on the shape or type of the object, depending on whether it is native.
if (obj->isNative()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
ImmGCPtr(obj->as<NativeObject>().lastProperty()),
failures);
} else {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()),
failures);
}
TestMatchingReceiver(masm, attacher, object, obj, failures);
// If we need a scratch register, use either an output register or the
// object register. After this point, we cannot jump directly to
@ -825,7 +851,10 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
bool restoreScratch = false;
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
if (obj != holder || !holder->isFixedSlot(shape->slot())) {
if (obj != holder ||
obj->is<UnboxedPlainObject>() ||
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
{
if (output.hasValue()) {
scratchReg = output.valueReg().scratchReg();
} else if (output.type() == MIRType_Double) {
@ -839,7 +868,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
// Fast path: single failure jump, no prototype guards.
if (!multipleFailureJumps) {
EmitLoadSlot(masm, holder, shape, object, output, scratchReg);
EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, object, output, scratchReg);
if (restoreScratch)
masm.pop(scratchReg);
attacher.jumpRejoin(masm);
@ -860,7 +889,7 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
masm.branchPtr(Assembler::NotEqual,
Address(holderReg, JSObject::offsetOfShape()),
ImmGCPtr(holder->lastProperty()),
ImmGCPtr(holder->as<NativeObject>().lastProperty()),
&prototypeFailures);
} else {
// The property does not exist. Guard on everything in the
@ -883,13 +912,17 @@ GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
holderReg = InvalidReg;
}
} else if (obj->is<UnboxedPlainObject>()) {
holder = obj->as<UnboxedPlainObject>().maybeExpando();
holderReg = scratchReg;
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
} else {
holderReg = object;
}
// Slot access.
if (holder)
EmitLoadSlot(masm, holder, shape, holderReg, output, scratchReg);
EmitLoadSlot(masm, &holder->as<NativeObject>(), shape, holderReg, output, scratchReg);
else
masm.moveValue(UndefinedValue(), output.valueReg());
@ -1098,15 +1131,6 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
return true;
}
static void
TestMatchingReceiver(MacroAssembler &masm, Register object, JSObject *obj, Label *failure)
{
if (Shape *shape = obj->maybeShape())
masm.branchTestObjShape(Assembler::NotEqual, object, shape, failure);
else
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
}
static bool
GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
@ -1119,7 +1143,7 @@ GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
Label stubFailure;
failures = failures ? failures : &stubFailure;
TestMatchingReceiver(masm, object, obj, failures);
TestMatchingReceiver(masm, attacher, object, obj, failures);
Register scratchReg = output.valueReg().scratchReg();
bool spillObjReg = scratchReg == object;
@ -1428,6 +1452,35 @@ GetPropertyIC::tryAttachUnboxed(JSContext *cx, HandleScript outerScript, IonScri
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed");
}
bool
GetPropertyIC::tryAttachUnboxedExpando(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted)
{
MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(outerScript->ionScript() == ion);
if (!obj->is<UnboxedPlainObject>())
return true;
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
if (!expando)
return true;
Shape *shape = expando->lookup(cx, name);
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
GenerateReadSlot(cx, ion, masm, attacher, obj, obj,
shape, object(), output());
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando");
}
bool
GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name, bool *emitted)
@ -1867,6 +1920,9 @@ GetPropertyIC::tryAttachStub(JSContext *cx, HandleScript outerScript, IonScript
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
return false;
@ -1997,37 +2053,33 @@ CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id,
static void
GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
NativeObject *obj, Shape *shape, Register object, ConstantOrRegister value,
JSObject *obj, Shape *shape, Register object, ConstantOrRegister value,
bool needsTypeBarrier, bool checkTypeset)
{
MOZ_ASSERT(obj->isNative());
Label failures, failurePopObject;
Label failures, barrierFailure;
masm.branchPtr(Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
ImmGCPtr(obj->lastProperty()), &failures);
TestMatchingReceiver(masm, attacher, object, obj, &failures, needsTypeBarrier);
// Guard that the incoming value is in the type set for the property
// if a type barrier is required.
if (needsTypeBarrier) {
// We can't do anything that would change the HeapTypeSet, so
// just guard that it's already there.
// Obtain and guard on the ObjectGroup of the object.
ObjectGroup *group = obj->group();
masm.branchPtr(Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(group), &failures);
if (checkTypeset) {
masm.push(object);
CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure);
CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &failurePopObject);
masm.pop(object);
}
}
NativeObject::slotsSizeMustNotOverflow();
if (obj->isFixedSlot(shape->slot())) {
if (obj->is<UnboxedPlainObject>()) {
obj = obj->as<UnboxedPlainObject>().maybeExpando();
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
}
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
if (cx->zone()->needsIncrementalBarrier())
@ -2038,7 +2090,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
Register slotsReg = object;
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value));
Address addr(slotsReg, obj->as<NativeObject>().dynamicSlotIndex(shape->slot()) * sizeof(Value));
if (cx->zone()->needsIncrementalBarrier())
masm.callPreBarrier(addr, MIRType_Value);
@ -2048,8 +2100,8 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
attacher.jumpRejoin(masm);
if (barrierFailure.used()) {
masm.bind(&barrierFailure);
if (failurePopObject.used()) {
masm.bind(&failurePopObject);
masm.pop(object);
}
@ -2059,7 +2111,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
bool
SetPropertyIC::attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleNativeObject obj, HandleShape shape, bool checkTypeset)
HandleObject obj, HandleShape shape, bool checkTypeset)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
@ -2663,7 +2715,7 @@ SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScri
RepatchStubAppender attacher(*this);
Label failure;
TestMatchingReceiver(masm, object(), obj, &failure);
TestMatchingReceiver(masm, attacher, object(), obj, &failure);
if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
object(), value(), &failure, liveRegs_, returnAddr))
@ -2683,31 +2735,42 @@ SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScri
static void
GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
NativeObject *obj, Shape *oldShape, ObjectGroup *oldGroup,
JSObject *obj, Shape *oldShape, ObjectGroup *oldGroup,
Register object, ConstantOrRegister value,
bool checkTypeset)
{
MOZ_ASSERT(obj->isNative());
Label failures, failuresPopObject;
Label failures;
// Use a modified version of TestMatchingReceiver that uses the old shape and group.
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, &failures);
if (obj->maybeShape()) {
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures);
} else {
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
// Guard the type of the object
masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(oldGroup), &failures);
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), &failures);
// Guard shapes along prototype chain.
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures);
masm.push(object);
masm.loadPtr(expandoAddress, object);
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failuresPopObject);
masm.pop(object);
}
Shape *newShape = obj->maybeShape();
if (!newShape)
newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
Label failuresPopObject;
masm.push(object); // save object reg because we clobber it
// Guard that the incoming value is in the type set for the property
// if a type barrier is required.
if (checkTypeset) {
CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject);
CheckTypeSetForWrite(masm, obj, newShape->propid(), object, value, &failuresPopObject);
masm.loadPtr(Address(StackPointer, 0), object);
}
// Guard shapes along prototype chain.
JSObject *proto = obj->getProto();
Register protoReg = object;
while (proto) {
@ -2724,14 +2787,19 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
masm.pop(object); // restore object reg
// Changing object shape. Write the object's new shape.
Shape *newShape = obj->lastProperty();
// Write the object or expando object's new shape.
if (obj->is<UnboxedPlainObject>()) {
obj = obj->as<UnboxedPlainObject>().maybeExpando();
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
}
Address shapeAddr(object, JSObject::offsetOfShape());
if (cx->zone()->needsIncrementalBarrier())
masm.callPreBarrier(shapeAddr, MIRType_Shape);
masm.storePtr(ImmGCPtr(newShape), shapeAddr);
if (oldGroup != obj->group()) {
MOZ_ASSERT(!obj->is<UnboxedPlainObject>());
// Changing object's group from a partially to fully initialized group,
// per the acquired properties analysis. Only change the group if the
// old group still has a newScript.
@ -2759,7 +2827,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
// Set the value on the object. Since this is an add, obj->lastProperty()
// must be the shape of the property we are adding.
NativeObject::slotsSizeMustNotOverflow();
if (obj->isFixedSlot(newShape->slot())) {
if (obj->as<NativeObject>().isFixedSlot(newShape->slot())) {
Address addr(object, NativeObject::getFixedSlotOffset(newShape->slot()));
masm.storeConstantOrRegister(value, addr);
} else {
@ -2767,7 +2835,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value));
Address addr(slotsReg, obj->as<NativeObject>().dynamicSlotIndex(newShape->slot()) * sizeof(Value));
masm.storeConstantOrRegister(value, addr);
}
@ -2784,7 +2852,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att
bool
SetPropertyIC::attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
bool checkTypeset)
{
MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
@ -2860,7 +2928,33 @@ IsPropertySetInlineable(NativeObject *obj, HandleId id, MutableHandleShape pshap
}
static bool
IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val, uint32_t oldSlots,
PrototypeChainShadowsPropertyAdd(JSObject *obj, jsid id)
{
// Walk up the object prototype chain and ensure that all prototypes
// are native, and that all prototypes have no getter or setter
// defined on the property
for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
// If prototype is non-native, don't optimize
if (!proto->isNative())
return true;
// If prototype defines this property in a non-plain way, don't optimize
Shape *protoShape = proto->as<NativeObject>().lookupPure(id);
if (protoShape && !protoShape->hasDefaultSetter())
return true;
// Otherwise, if there's no such property, watch out for a resolve
// hook that would need to be invoked and thus prevent inlining of
// property addition.
if (proto->getClass()->resolve)
return true;
}
return false;
}
static bool
IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val,
HandleShape oldShape, bool needsTypeBarrier, bool *checkTypeset)
{
// If the shape of the object did not change, then this was not an add.
@ -2886,35 +2980,18 @@ IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val,
if (!obj->nonProxyIsExtensible() || !shape->writable())
return false;
// Walk up the object prototype chain and ensure that all prototypes
// are native, and that all prototypes have no getter or setter
// defined on the property
for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
// If prototype is non-native, don't optimize
if (!proto->isNative())
return false;
// If prototype defines this property in a non-plain way, don't optimize
Shape *protoShape = proto->as<NativeObject>().lookupPure(id);
if (protoShape && !protoShape->hasDefaultSetter())
return false;
// Otherwise, if there's no such property, watch out for a resolve
// hook that would need to be invoked and thus prevent inlining of
// property addition.
if (proto->getClass()->resolve)
return false;
}
if (PrototypeChainShadowsPropertyAdd(obj, id))
return false;
// 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 (obj->numDynamicSlots() != oldSlots)
if (obj->numDynamicSlots() != NativeObject::dynamicSlotsCount(oldShape))
return false;
// Don't attach if we are adding a property to an object which the new
// script properties analysis hasn't been performed for yet, as there
// may be a shape change required here afterwards.
// may be a group change required here afterwards.
if (obj->group()->newScript() && !obj->group()->newScript()->analyzed())
return false;
@ -2946,7 +3023,7 @@ CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrR
// a stub to add the property until we do the VM call to add. If the
// property exists as a data property on the prototype, we should add
// a new, shadowing property.
if (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot()))
if (obj->isNative() && (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot())))
return SetPropertyIC::MaybeCanAttachAddSlot;
if (IsImplicitNonNativeProperty(shape))
@ -3041,6 +3118,58 @@ CanAttachSetUnboxed(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegi
return false;
}
static bool
CanAttachSetUnboxedExpando(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val,
bool needsTypeBarrier, bool *checkTypeset, Shape **pshape)
{
if (!obj->is<UnboxedPlainObject>())
return false;
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
if (!expando)
return false;
Shape *shape = expando->lookupPure(id);
if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
return false;
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
return false;
*pshape = shape;
return true;
}
static bool
CanAttachAddUnboxedExpando(JSContext *cx, HandleObject obj, HandleShape oldShape,
HandleId id, ConstantOrRegister val,
bool needsTypeBarrier, bool *checkTypeset)
{
if (!obj->is<UnboxedPlainObject>())
return false;
Rooted<UnboxedExpandoObject *> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
if (!expando || expando->inDictionaryMode())
return false;
Shape *newShape = expando->lastProperty();
if (newShape->propid() != id || newShape->previous() != oldShape)
return false;
MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
if (PrototypeChainShadowsPropertyAdd(obj, id))
return false;
if (NativeObject::dynamicSlotsCount(oldShape) != NativeObject::dynamicSlotsCount(newShape))
return false;
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
return false;
return true;
}
bool
SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
HandleValue value)
@ -3109,9 +3238,9 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
checkTypeset = false;
uint32_t unboxedOffset;
JSValueType unboxedType;
if (!addedSetterStub && CanAttachSetUnboxed(cx, obj, id, cache.value(),
cache.needsTypeBarrier(),
&checkTypeset, &unboxedOffset, &unboxedType))
if (!addedSetterStub &&
CanAttachSetUnboxed(cx, obj, id, cache.value(), cache.needsTypeBarrier(),
&checkTypeset, &unboxedOffset, &unboxedType))
{
if (!cache.attachSetUnboxed(cx, outerScript, ion, obj, id, unboxedOffset, unboxedType,
checkTypeset))
@ -3120,10 +3249,23 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
}
addedSetterStub = true;
}
checkTypeset = false;
if (!addedSetterStub &&
CanAttachSetUnboxedExpando(cx, obj, id, cache.value(), cache.needsTypeBarrier(),
&checkTypeset, shape.address()))
{
if (!cache.attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
return false;
addedSetterStub = true;
}
}
uint32_t oldSlots = obj->is<NativeObject>() ? obj->as<NativeObject>().numDynamicSlots() : 0;
RootedShape oldShape(cx, obj->maybeShape());
if (!oldShape) {
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando())
oldShape = expando->lastProperty();
}
// Set/Add the property on the object, the inlined cache are setup for the next execution.
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
@ -3132,12 +3274,20 @@ SetPropertyIC::update(JSContext *cx, HandleScript outerScript, size_t cacheIndex
// The property did not exist before, now we can try to inline the property add.
bool checkTypeset;
if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
IsPropertyAddInlineable(&obj->as<NativeObject>(), id,
cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
&checkTypeset))
IsPropertyAddInlineable(&obj->as<NativeObject>(), id, cache.value(), oldShape,
cache.needsTypeBarrier(), &checkTypeset))
{
RootedNativeObject nobj(cx, &obj->as<NativeObject>());
if (!cache.attachAddSlot(cx, outerScript, ion, nobj, oldShape, oldGroup, checkTypeset))
if (!cache.attachAddSlot(cx, outerScript, ion, obj, oldShape, oldGroup, checkTypeset))
return false;
addedSetterStub = true;
}
checkTypeset = false;
if (!addedSetterStub && cache.canAttachStub() &&
CanAttachAddUnboxedExpando(cx, obj, oldShape, id, cache.value(),
cache.needsTypeBarrier(), &checkTypeset))
{
if (!cache.attachAddSlot(cx, outerScript, ion, obj, oldShape, oldGroup, checkTypeset))
return false;
addedSetterStub = true;
}

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

@ -670,6 +670,10 @@ class GetPropertyIC : public RepatchIonCache
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted);
bool tryAttachUnboxedExpando(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name,
void *returnAddr, bool *emitted);
bool tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandlePropertyName name, bool *emitted);
@ -739,14 +743,14 @@ class SetPropertyIC : public RepatchIonCache
};
bool attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleNativeObject obj, HandleShape shape, bool checkTypeset);
HandleObject obj, HandleShape shape, bool checkTypeset);
bool attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandleObject holder, HandleShape shape,
void *returnAddr);
bool attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleNativeObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup,
bool checkTypeset);
bool attachSetUnboxed(JSContext *cx, HandleScript outerScript, IonScript *ion,

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

@ -4909,7 +4909,8 @@ AddGroupGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj,
if (key->isGroup()) {
guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
Bailout_ObjectIdentityOrTypeGuard);
Bailout_ObjectIdentityOrTypeGuard,
/* checkUnboxedExpando = */ false);
} else {
MConstant *singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
current->add(singletonConst);

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

@ -10157,13 +10157,15 @@ class MGuardObjectGroup
AlwaysTenured<ObjectGroup *> group_;
bool bailOnEquality_;
BailoutKind bailoutKind_;
bool checkUnboxedExpando_;
MGuardObjectGroup(MDefinition *obj, ObjectGroup *group, bool bailOnEquality,
BailoutKind bailoutKind)
BailoutKind bailoutKind, bool checkUnboxedExpando)
: MUnaryInstruction(obj),
group_(group),
bailOnEquality_(bailOnEquality),
bailoutKind_(bailoutKind)
bailoutKind_(bailoutKind),
checkUnboxedExpando_(checkUnboxedExpando)
{
setGuard();
setMovable();
@ -10178,8 +10180,10 @@ class MGuardObjectGroup
INSTRUCTION_HEADER(GuardObjectGroup)
static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, ObjectGroup *group,
bool bailOnEquality, BailoutKind bailoutKind) {
return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind);
bool bailOnEquality, BailoutKind bailoutKind,
bool checkUnboxedExpando) {
return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind,
checkUnboxedExpando);
}
MDefinition *obj() const {
@ -10194,6 +10198,9 @@ class MGuardObjectGroup
BailoutKind bailoutKind() const {
return bailoutKind_;
}
bool checkUnboxedExpando() const {
return checkUnboxedExpando_;
}
bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
if (!ins->isGuardObjectGroup())
return false;

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

@ -1281,8 +1281,7 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
offset += sizeof(uintptr_t);
}
} else if (templateObj->is<UnboxedPlainObject>()) {
storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
if (initContents)
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
} else {

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

@ -1662,6 +1662,13 @@ CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup *guard)
{
Register obj = ToRegister(guard->input());
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()));

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

@ -1751,6 +1751,12 @@ CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup *guard)
{
Register obj = ToRegister(guard->input());
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()

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

@ -2070,6 +2070,12 @@ void
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 =

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

@ -2931,7 +2931,7 @@ js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **
// are not handled here.
if (!obj->is<UnboxedPlainObject>())
return false;
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
*objp = obj;
MarkNonNativePropertyFound<NoGC>(propp);
return true;

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

@ -626,6 +626,12 @@ class NativeObject : public JSObject
return lookup(cx, shape->propid()) == shape;
}
bool containsShapeOrElement(ExclusiveContext *cx, jsid id) {
if (JSID_IS_INT(id) && containsDenseElement(JSID_TO_INT(id)))
return true;
return contains(cx, id);
}
/* Contextless; can be called from other pure code. */
Shape *lookupPure(jsid id);
Shape *lookupPure(PropertyName *name) {
@ -874,6 +880,9 @@ class NativeObject : public JSObject
* array is kept in sync with this count.
*/
static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp);
static uint32_t dynamicSlotsCount(Shape *shape) {
return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), shape->getObjectClass());
}
/* Elements accessors. */

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

@ -428,7 +428,8 @@ class ObjectGroup : public gc::TenuredCell
* objects, property types account for plain data properties (those with a
* slot and no getter or setter hook) and dense elements. In typed objects
* and unboxed objects, property types account for object and value
* properties and elements in the object.
* properties and elements in the object, and expando properties in unboxed
* objects.
*
* For accesses on these properties, the correspondence is as follows:
*

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

@ -346,6 +346,11 @@ UnboxedPlainObject::getValue(const UnboxedLayout::Property &property)
void
UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
{
if (obj->as<UnboxedPlainObject>().expando_) {
MarkObjectUnbarriered(trc, reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
"unboxed_expando");
}
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
const int32_t *list = layout.traceList();
if (!list)
@ -369,6 +374,39 @@ UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
MOZ_ASSERT(*(list + 1) == -1);
}
/* static */ UnboxedExpandoObject *
UnboxedPlainObject::ensureExpando(JSContext *cx, Handle<UnboxedPlainObject *> obj)
{
if (obj->expando_)
return obj->expando_;
UnboxedExpandoObject *expando = NewObjectWithGivenProto<UnboxedExpandoObject>(cx, NullPtr());
if (!expando)
return nullptr;
// As with setValue(), we need to manually trigger post barriers on the
// whole object. If we treat the field as a HeapPtrObject and later convert
// the object to its native representation, we will end up with a corrupted
// store buffer entry.
if (IsInsideNursery(expando) && !IsInsideNursery(obj))
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(obj);
obj->expando_ = expando;
return expando;
}
bool
UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext *cx, jsid id) const
{
if (layout().lookup(id))
return true;
if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
return true;
return false;
}
/* static */ bool
UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
{
@ -471,6 +509,7 @@ UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
{
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!layout.nativeGroup()) {
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
@ -493,6 +532,41 @@ UnboxedPlainObject::convertToNative(JSContext *cx, JSObject *obj)
for (size_t i = 0; i < values.length(); i++)
obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
if (expando) {
// Add properties from the expando object to the object, in order.
// Suppress GC here, so that callers don't need to worry about this
// method collecting. The stuff below can only fail due to OOM, in
// which case the object will not have been completely filled back in.
gc::AutoSuppressGC suppress(cx);
Vector<jsid> ids(cx);
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
if (!ids.append(r.front().propid()))
return false;
}
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
if (!ids.append(INT_TO_JSID(i)))
return false;
}
}
::Reverse(ids.begin(), ids.end());
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
Rooted<UnboxedExpandoObject *> nexpando(cx, expando);
RootedId id(cx);
Rooted<PropertyDescriptor> desc(cx);
for (size_t i = 0; i < ids.length(); i++) {
id = ids[i];
if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
return false;
ObjectOpResult result;
if (!StandardDefineProperty(cx, nobj, id, desc, result))
return false;
MOZ_ASSERT(result.ok());
}
}
return true;
}
@ -508,8 +582,8 @@ UnboxedPlainObject::create(ExclusiveContext *cx, HandleObjectGroup group, NewObj
if (!res)
return nullptr;
// Avoid spurious shape guard hits.
res->dummy_ = nullptr;
// Overwrite the dummy shape which was written to the object's expando field.
res->initExpando();
// Initialize reference fields of the object. All fields in the object will
// be overwritten shortly, but references need to be safe for the GC.
@ -585,7 +659,7 @@ UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
MutableHandleShape propp)
{
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
return true;
@ -606,16 +680,38 @@ UnboxedPlainObject::obj_defineProperty(JSContext *cx, HandleObject obj, HandleId
GetterOp getter, SetterOp setter, unsigned attrs,
ObjectOpResult &result)
{
if (!convertToNative(cx, obj))
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
if (const UnboxedLayout::Property *property = layout.lookup(id)) {
if (!getter && !setter && attrs == JSPROP_ENUMERATE) {
// This define is equivalent to setting an existing property.
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
return true;
}
// Trying to incompatibly redefine an existing property requires the
// object to be converted to a native object.
if (!convertToNative(cx, obj))
return false;
return DefineProperty(cx, obj, id, v, getter, setter, attrs);
}
// Define the property on the expando object.
Rooted<UnboxedExpandoObject *> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
if (!expando)
return false;
return DefineProperty(cx, obj, id, v, getter, setter, attrs, result);
// Update property types on the unboxed object as well.
AddTypePropertyId(cx, obj, id, v);
return DefineProperty(cx, expando, id, v, getter, setter, attrs, result);
}
/* static */ bool
UnboxedPlainObject::obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool *foundp)
{
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
*foundp = true;
return true;
}
@ -640,6 +736,14 @@ UnboxedPlainObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObjec
return true;
}
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
if (expando->containsShapeOrElement(cx, id)) {
RootedObject nexpando(cx, expando);
RootedObject nreceiver(cx, (obj == receiver) ? expando : receiver.get());
return GetProperty(cx, nexpando, nreceiver, id, vp);
}
}
RootedObject proto(cx, obj->getProto());
if (!proto) {
vp.setUndefined();
@ -668,6 +772,17 @@ UnboxedPlainObject::obj_setProperty(JSContext *cx, HandleObject obj, HandleObjec
return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result);
}
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
if (expando->containsShapeOrElement(cx, id)) {
// Update property types on the unboxed object as well.
AddTypePropertyId(cx, obj, id, vp);
RootedObject nexpando(cx, expando);
RootedObject nreceiver(cx, (obj == receiver) ? expando : receiver.get());
return SetProperty(cx, nexpando, nreceiver, id, vp, result);
}
}
return SetPropertyOnProto(cx, obj, receiver, id, vp, result);
}
@ -684,6 +799,13 @@ UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj
return true;
}
if (UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
if (expando->containsShapeOrElement(cx, id)) {
RootedObject nexpando(cx, expando);
return GetOwnPropertyDescriptor(cx, nexpando, id, desc);
}
}
desc.object().set(nullptr);
return true;
}
@ -708,16 +830,45 @@ UnboxedPlainObject::obj_watch(JSContext *cx, HandleObject obj, HandleId id, Hand
/* static */ bool
UnboxedPlainObject::obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties)
{
UnboxedExpandoObject *expando = obj->as<UnboxedPlainObject>().maybeExpando();
// Add dense elements in the expando first, for consistency with plain objects.
if (expando) {
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
if (!properties.append(INT_TO_JSID(i)))
return false;
}
}
}
const UnboxedLayout::PropertyVector &unboxed = obj->as<UnboxedPlainObject>().layout().properties();
for (size_t i = 0; i < unboxed.length(); i++) {
if (!properties.append(NameToId(unboxed[i].name)))
return false;
}
if (expando) {
Vector<jsid> ids(cx);
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
if (!ids.append(r.front().propid()))
return false;
}
::Reverse(ids.begin(), ids.end());
if (!properties.append(ids.begin(), ids.length()))
return false;
}
return true;
}
const Class UnboxedExpandoObject::class_ = {
"UnboxedExpandoObject",
JSCLASS_IMPLEMENTS_BARRIERS
};
const Class UnboxedPlainObject::class_ = {
"Object",
js_Object_str,
Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS,
nullptr, /* addProperty */
nullptr, /* delProperty */
@ -1000,6 +1151,7 @@ js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
if (!objects->get(i))
continue;
UnboxedPlainObject *obj = &objects->get(i)->as<UnboxedPlainObject>();
obj->initExpando();
memset(obj->data(), 0, layout->size());
for (size_t j = 0; j < templateShape->slotSpan(); j++) {
Value v = values[valueCursor++];

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

@ -162,14 +162,25 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
static bool makeConstructorCode(JSContext *cx, HandleObjectGroup group);
};
// Class for expando objects holding extra properties given to an unboxed plain
// object. These objects behave identically to normal native plain objects, and
// have a separate Class to distinguish them for memory usage reporting.
class UnboxedExpandoObject : public NativeObject
{
public:
static const Class class_;
};
// Class for a plain object using an unboxed representation. The physical
// layout of these objects is identical to that of an InlineTypedObject, though
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
// how their properties are stored.
class UnboxedPlainObject : public JSObject
{
// Placeholder for extra properties. See bug 1137180.
void *dummy_;
// Optional object which stores extra properties on this object. This is
// not automatically barriered to avoid problems if the object is converted
// to a native. See ensureExpando().
UnboxedExpandoObject *expando_;
// Start of the inline data, which immediately follows the group and extra properties.
uint8_t data_[1];
@ -214,6 +225,18 @@ class UnboxedPlainObject : public JSObject
return &data_[0];
}
UnboxedExpandoObject *maybeExpando() const {
return expando_;
}
void initExpando() {
expando_ = nullptr;
}
bool containsUnboxedOrExpandoProperty(ExclusiveContext *cx, jsid id) const;
static UnboxedExpandoObject *ensureExpando(JSContext *cx, Handle<UnboxedPlainObject *> obj);
bool setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property, const Value &v);
Value getValue(const UnboxedLayout::Property &property);
@ -225,6 +248,10 @@ class UnboxedPlainObject : public JSObject
static void trace(JSTracer *trc, JSObject *object);
static size_t offsetOfExpando() {
return offsetof(UnboxedPlainObject, expando_);
}
static size_t offsetOfData() {
return offsetof(UnboxedPlainObject, data_[0]);
}