Revert #1142 - Remove unboxed objects

- accounting for removal of watch()/unwatch()
This commit is contained in:
wolfbeast 2020-02-22 21:09:32 +01:00 • committed by Roy Tam
commit 47781d6165
48 changed files with 3400 additions and 75 deletions

View file

@ -31,6 +31,7 @@
#include "jit/shared/Lowering-shared-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/Shape-inl.h"
#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@ -619,7 +620,26 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
Register object, JSObject* obj, Label* failure,
bool alwaysCheckGroup = false)
{
if (obj->is<TypedObject>()) {
if (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);
}
} else if (obj->is<TypedObject>()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()), failure);
@ -736,6 +756,7 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
// jump directly. Otherwise, jump to the end of the stub, so there's a
// common point to patch.
bool multipleFailureJumps = (obj != holder)
|| obj->is<UnboxedPlainObject>()
|| (checkTDZ && output.hasValue())
|| (failures != nullptr && failures->used());
@ -754,6 +775,7 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
if (obj != holder ||
obj->is<UnboxedPlainObject>() ||
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
{
if (output.hasValue()) {
@ -814,6 +836,10 @@ 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;
}
@ -841,6 +867,30 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
attacher.jumpNextStub(masm);
}
static void
GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm,
IonCache::StubAttacher& attacher, JSObject* obj,
const UnboxedLayout::Property* property,
Register object, TypedOrValueRegister output,
Label* failures = nullptr)
{
// Guard on the group of the object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()), failures);
Address address(object, UnboxedPlainObject::offsetOfData() + property->offset);
masm.loadUnboxedProperty(address, property->type, output);
attacher.jumpRejoin(masm);
if (failures) {
masm.bind(failures);
attacher.jumpNextStub(masm);
}
}
static bool
EmitGetterCall(JSContext* cx, MacroAssembler& masm,
IonCache::StubAttacher& attacher, JSObject* obj,
@ -1447,6 +1497,67 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
return linkAndAttachStub(cx, masm, attacher, ion, attachKind, outcome);
}
bool
GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, void* returnAddr, bool* emitted)
{
MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(outerScript->ionScript() == ion);
if (!obj->is<UnboxedPlainObject>())
return true;
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
if (!property)
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
Label failures;
emitIdGuard(masm, id, &failures);
Label* maybeFailures = failures.used() ? &failures : nullptr;
StubAttacher attacher(*this);
GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures);
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed",
JS::TrackedOutcome::ICGetPropStub_UnboxedRead);
}
bool
GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, 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, id);
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
Label failures;
emitIdGuard(masm, id, &failures);
Label* maybeFailures = failures.used() ? &failures : nullptr;
StubAttacher attacher(*this);
GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj,
shape, object(), output(), maybeFailures);
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando",
JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando);
}
bool
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
@ -2016,6 +2127,12 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted))
return false;
}
@ -2194,6 +2311,12 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
NativeObject::slotsSizeMustNotOverflow();
if (obj->is<UnboxedPlainObject>()) {
obj = obj->as<UnboxedPlainObject>().maybeExpando();
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg);
object = tempReg;
}
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
@ -2831,13 +2954,23 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
if (obj->maybeShape()) {
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
} else {
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures);
masm.loadPtr(expandoAddress, tempReg);
masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures);
}
Shape* newShape = obj->maybeShape();
if (!newShape)
newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
// Guard that the incoming value is in the type set for the property
// if a type barrier is required.
if (newShape && checkTypeset)
if (checkTypeset)
CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
// Guard shapes along prototype chain.
@ -2858,7 +2991,9 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
}
// Call a stub to (re)allocate dynamic slots, if necessary.
uint32_t newNumDynamicSlots = obj->as<NativeObject>().numDynamicSlots();
uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>()
? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots()
: obj->as<NativeObject>().numDynamicSlots();
if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
AllocatableRegisterSet regs(RegisterSet::Volatile());
LiveRegisterSet save(regs.asLiveSet());
@ -2869,6 +3004,12 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
Register temp1 = regs.takeAnyGeneral();
Register temp2 = regs.takeAnyGeneral();
if (obj->is<UnboxedPlainObject>()) {
// Pass the expando object to the stub.
masm.Push(object);
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
}
masm.setupUnalignedABICall(temp1);
masm.loadJSContext(temp1);
masm.passABIArg(temp1);
@ -2885,16 +3026,27 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
masm.jump(&allocDone);
masm.bind(&allocFailed);
if (obj->is<UnboxedPlainObject>())
masm.Pop(object);
masm.PopRegsInMask(save);
masm.jump(failures);
masm.bind(&allocDone);
masm.setFramePushed(framePushedAfterCall);
if (obj->is<UnboxedPlainObject>())
masm.Pop(object);
masm.PopRegsInMask(save);
}
bool popObject = false;
if (obj->is<UnboxedPlainObject>()) {
masm.push(object);
popObject = true;
obj = obj->as<UnboxedPlainObject>().maybeExpando();
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
}
// Write the object or expando object's new shape.
Address shapeAddr(object, ShapedObject::offsetOfShape());
if (cx->zone()->needsIncrementalBarrier())
@ -2902,6 +3054,8 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
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.
@ -3144,6 +3298,141 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const
return SetPropertyIC::CanAttachNone;
}
static void
GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
Register object, Register tempReg, const ConstantOrRegister& value,
bool checkTypeset, Label* failures)
{
// Guard on the type of the object.
masm.branchPtr(Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()), failures);
if (checkTypeset)
CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures);
Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset);
if (cx->zone()->needsIncrementalBarrier()) {
if (unboxedType == JSVAL_TYPE_OBJECT)
masm.callPreBarrier(address, MIRType::Object);
else if (unboxedType == JSVAL_TYPE_STRING)
masm.callPreBarrier(address, MIRType::String);
else
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
}
masm.storeUnboxedProperty(address, unboxedType, value, failures);
attacher.jumpRejoin(masm);
masm.bind(failures);
attacher.jumpNextStub(masm);
}
static bool
CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
bool needsTypeBarrier, bool* checkTypeset,
uint32_t* unboxedOffset, JSValueType* unboxedType)
{
if (!obj->is<UnboxedPlainObject>())
return false;
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
if (property) {
*checkTypeset = false;
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
return false;
*unboxedOffset = property->offset;
*unboxedType = property->type;
return true;
}
return false;
}
static bool
CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id,
const 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;
*checkTypeset = 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, const 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->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape)
return false;
MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
return false;
*checkTypeset = false;
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
return false;
return true;
}
bool
SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
{
MOZ_ASSERT(!*emitted);
bool checkTypeset = false;
uint32_t unboxedOffset;
JSValueType unboxedType;
if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset,
&unboxedOffset, &unboxedType))
{
return true;
}
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
Label failures;
emitIdGuard(masm, id, &failures);
GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType,
object(), temp(), value(), checkTypeset, &failures);
return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed",
JS::TrackedOutcome::ICSetPropStub_SetUnboxed);
}
bool
SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
@ -3224,6 +3513,26 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
MOZ_CRASH("Unreachable");
}
bool
SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
{
MOZ_ASSERT(!*emitted);
RootedShape shape(cx);
bool checkTypeset = false;
if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(),
&checkTypeset, shape.address()))
{
return true;
}
if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
return false;
*emitted = true;
return true;
}
bool
SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleValue idval, HandleValue value,
@ -3249,6 +3558,12 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot))
return false;
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted))
return false;
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted))
return false;
}
if (idval.isInt32()) {
@ -3300,6 +3615,16 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri
return true;
}
checkTypeset = false;
if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(),
&checkTypeset))
{
if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset))
return false;
*emitted = true;
return true;
}
return true;
}
@ -3321,6 +3646,11 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
return false;
oldShape = obj->maybeShape();
if (obj->is<UnboxedPlainObject>()) {
MOZ_ASSERT(!oldShape);
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
oldShape = expando->lastProperty();
}
}
RootedId id(cx);