Bug 1505574 - Remove Unboxed Objects from jit/ - Part 2 r=iain

Differential Revision: https://phabricator.services.mozilla.com/D24045

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matthew Gaudet 2019-03-22 15:32:34 +00:00
parent 991590f001
commit eb0619d3c4
14 changed files with 56 additions and 1236 deletions

View File

@ -730,20 +730,6 @@ bool BaselineCacheIRCompiler::emitCallNativeGetElementResult() {
return true;
}
bool BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
JSValueType fieldType = reader.jsValueType();
Address fieldOffset(stubAddress(reader.stubOffset()));
masm.load32(fieldOffset, scratch);
masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType,
output);
return true;
}
bool BaselineCacheIRCompiler::emitGuardFrameHasNoArgumentsObject() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
FailurePath* failure;
@ -1180,46 +1166,6 @@ bool BaselineCacheIRCompiler::emitAllocateAndStoreDynamicSlot() {
return emitAddAndStoreSlotShared(CacheOp::AllocateAndStoreDynamicSlot);
}
bool BaselineCacheIRCompiler::emitStoreUnboxedProperty() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
ObjOperandId objId = reader.objOperandId();
JSValueType fieldType = reader.jsValueType();
Address offsetAddr = stubAddress(reader.stubOffset());
// Allocate the fixed registers first. These need to be fixed for
// callTypeUpdateIC.
AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
ValueOperand val =
allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
Register obj = allocator.useRegister(masm, objId);
// We only need the type update IC if we are storing an object.
if (fieldType == JSVAL_TYPE_OBJECT) {
LiveGeneralRegisterSet saveRegs;
saveRegs.add(obj);
saveRegs.add(val);
if (!callTypeUpdateIC(obj, val, scratch, saveRegs)) {
return false;
}
}
masm.load32(offsetAddr, scratch);
BaseIndex fieldAddr(obj, scratch, TimesOne);
// Note that the storeUnboxedProperty call here is infallible, as the
// IR emitter is responsible for guarding on |val|'s type.
EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
masm.storeUnboxedProperty(fieldAddr, fieldType,
ConstantOrRegister(TypedOrValueRegister(val)),
/* failure = */ nullptr);
if (UnboxedTypeNeedsPostBarrier(fieldType)) {
emitPostBarrierSlot(obj, val, scratch);
}
return true;
}
bool BaselineCacheIRCompiler::emitStoreTypedObjectReferenceProperty() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
ObjOperandId objId = reader.objOperandId();

View File

@ -59,26 +59,18 @@ static bool VectorAppendNoDuplicate(S& list, T value) {
return list.append(value);
}
static bool AddReceiver(
const ReceiverGuard& receiver, BaselineInspector::ReceiverVector& receivers) {
static bool AddReceiver(const ReceiverGuard& receiver,
BaselineInspector::ReceiverVector& receivers) {
return VectorAppendNoDuplicate(receivers, receiver);
}
static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub,
ReceiverGuard* receiver) {
// We match either:
// We match:
//
// GuardIsObject 0
// GuardShape 0
// LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
//
// or
//
// GuardIsObject 0
// GuardGroup 0
// 1: GuardAndLoadUnboxedExpando 0
// GuardShape 1
// LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
*receiver = ReceiverGuard();
CacheIRReader reader(stub->stubInfo());
@ -88,16 +80,6 @@ static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub,
return false;
}
if (reader.matchOp(CacheOp::GuardGroup, objId)) {
receiver->group =
stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) {
return false;
}
objId = reader.objOperandId();
}
if (reader.matchOp(CacheOp::GuardShape, objId)) {
receiver->shape =
stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
@ -108,48 +90,14 @@ static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub,
return false;
}
static bool GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub,
ReceiverGuard* receiver) {
// We match:
//
// GuardIsObject 0
// GuardGroup 0
// LoadUnboxedPropertyResult 0 ..
*receiver = ReceiverGuard();
CacheIRReader reader(stub->stubInfo());
ObjOperandId objId = ObjOperandId(0);
if (!reader.matchOp(CacheOp::GuardIsObject, objId)) {
return false;
}
if (!reader.matchOp(CacheOp::GuardGroup, objId)) {
return false;
}
receiver->group =
stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId);
}
static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
ReceiverGuard* receiver) {
// We match either:
// We match:
//
// GuardIsObject 0
// GuardGroup 0
// GuardShape 0
// StoreFixedSlot 0 or StoreDynamicSlot 0
//
// or
//
// GuardIsObject 0
// GuardGroup 0
// 1: GuardAndLoadUnboxedExpando 0
// GuardShape 1
// StoreFixedSlot 1 or StoreDynamicSlot 1
*receiver = ReceiverGuard();
CacheIRReader reader(stub->stubInfo());
@ -164,10 +112,6 @@ static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
ObjectGroup* group =
stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
if (reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) {
objId = reader.objOperandId();
}
if (!reader.matchOp(CacheOp::GuardShape, objId)) {
return false;
}
@ -183,46 +127,6 @@ static bool GetCacheIRReceiverForNativeSetSlot(ICCacheIR_Updated* stub,
return true;
}
static bool GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Updated* stub,
ReceiverGuard* receiver) {
// We match:
//
// GuardIsObject 0
// GuardGroup 0
// GuardType 1 type | GuardIsObjectOrNull 1
// StoreUnboxedProperty 0
*receiver = ReceiverGuard();
CacheIRReader reader(stub->stubInfo());
ObjOperandId objId = ObjOperandId(0);
ValOperandId rhsId = ValOperandId(1);
if (!reader.matchOp(CacheOp::GuardIsObject, objId)) {
return false;
}
if (!reader.matchOp(CacheOp::GuardGroup, objId)) {
return false;
}
ObjectGroup* group =
stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
if (reader.matchOp(CacheOp::GuardType, rhsId)) {
reader.valueType(); // Skip.
} else {
if (!reader.matchOp(CacheOp::GuardIsObjectOrNull, rhsId)) {
return false;
}
}
if (!reader.matchOp(CacheOp::StoreUnboxedProperty)) {
return false;
}
*receiver = ReceiverGuard(group, nullptr);
return true;
}
ICScript* BaselineInspector::icScript() const { return script->icScript(); }
ICEntry& BaselineInspector::icEntryFromPC(jsbytecode* pc) {
@ -245,11 +149,11 @@ ICEntry* BaselineInspector::maybeICEntryFromPC(jsbytecode* pc) {
return ent;
}
bool BaselineInspector::maybeInfoForPropertyOp(
jsbytecode* pc, ReceiverVector& receivers) {
bool BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc,
ReceiverVector& receivers) {
// Return a list of the receivers seen by the baseline IC for the current
// op. Empty lists indicate no receivers are known, or there was an
// uncacheable access.
// uncacheable access.
MOZ_ASSERT(receivers.empty());
if (!hasICScript()) {
@ -264,17 +168,13 @@ bool BaselineInspector::maybeInfoForPropertyOp(
ReceiverGuard receiver;
if (stub->isCacheIR_Monitored()) {
if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(),
&receiver) &&
!GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(),
&receiver)) {
&receiver)) {
receivers.clear();
return true;
}
} else if (stub->isCacheIR_Updated()) {
if (!GetCacheIRReceiverForNativeSetSlot(stub->toCacheIR_Updated(),
&receiver) &&
!GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Updated(),
&receiver)) {
&receiver)) {
receivers.clear();
return true;
}
@ -1031,17 +931,6 @@ static bool MatchCacheIRReceiverGuard(CacheIRReader& reader, ICStub* stub,
//
// GuardShape objId
//
// or:
//
// GuardGroup objId
// [GuardNoUnboxedExpando objId]
//
// or:
//
// GuardGroup objId
// expandoId: GuardAndLoadUnboxedExpando
// GuardShape expandoId
*receiver = ReceiverGuard();
if (reader.matchOp(CacheOp::GuardShape, objId)) {
@ -1049,34 +938,15 @@ static bool MatchCacheIRReceiverGuard(CacheIRReader& reader, ICStub* stub,
receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
return true;
}
if (!reader.matchOp(CacheOp::GuardGroup, objId)) {
return false;
}
receiver->group =
stubInfo->getStubField<ObjectGroup*>(stub, reader.stubOffset());
if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) {
// Second case, just a group guard.
reader.matchOp(CacheOp::GuardNoUnboxedExpando, objId);
return true;
}
// Third case.
ObjOperandId expandoId = reader.objOperandId();
if (!reader.matchOp(CacheOp::GuardShape, expandoId)) {
return false;
}
receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
return true;
return false;
}
static bool AddCacheIRGlobalGetter(
ICCacheIR_Monitored* stub, bool innerized, JSObject** holder_,
Shape** holderShape_, JSFunction** commonGetter, Shape** globalShape_,
bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
JSScript* script) {
static bool AddCacheIRGlobalGetter(ICCacheIR_Monitored* stub, bool innerized,
JSObject** holder_, Shape** holderShape_,
JSFunction** commonGetter,
Shape** globalShape_, bool* isOwnProperty,
BaselineInspector::ReceiverVector& receivers,
JSScript* script) {
// We are matching on the IR generated by tryAttachGlobalNameGetter:
//
// GuardShape objId
@ -1376,9 +1246,9 @@ bool BaselineInspector::commonGetPropFunction(
for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
if (stub->isCacheIR_Monitored()) {
if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), id, innerized,
holder, holderShape, commonGetter,
globalShape, isOwnProperty, receivers, script)) {
if (!AddCacheIRGetPropFunction(
stub->toCacheIR_Monitored(), id, innerized, holder, holderShape,
commonGetter, globalShape, isOwnProperty, receivers, script)) {
return false;
}
} else if (stub->isFallback()) {
@ -1599,9 +1469,11 @@ static bool AddCacheIRSetPropFunction(
return true;
}
bool BaselineInspector::commonSetPropFunction(
jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers) {
bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder,
Shape** holderShape,
JSFunction** commonSetter,
bool* isOwnProperty,
ReceiverVector& receivers) {
if (!hasICScript()) {
return false;
}
@ -1693,8 +1565,9 @@ static bool GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored* stub,
return true;
}
bool BaselineInspector::maybeInfoForProtoReadSlot(
jsbytecode* pc, ReceiverVector& receivers, JSObject** holder) {
bool BaselineInspector::maybeInfoForProtoReadSlot(jsbytecode* pc,
ReceiverVector& receivers,
JSObject** holder) {
// This is like maybeInfoForPropertyOp, but for when the property exists on
// the prototype.

View File

@ -20,9 +20,9 @@
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypeInference-inl.h"
#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@ -268,12 +268,6 @@ bool GetPropIRGenerator::tryAttachStub() {
if (tryAttachNative(obj, objId, id)) {
return true;
}
if (tryAttachUnboxed(obj, objId, id)) {
return true;
}
if (tryAttachUnboxedExpando(obj, objId, id)) {
return true;
}
if (tryAttachTypedObject(obj, objId, id)) {
return true;
}
@ -322,9 +316,6 @@ bool GetPropIRGenerator::tryAttachStub() {
if (tryAttachSparseElement(obj, objId, index, indexId)) {
return true;
}
if (tryAttachUnboxedElementHole(obj, objId, index, indexId)) {
return true;
}
if (tryAttachArgumentsObjectArg(obj, objId, indexId)) {
return true;
}
@ -519,11 +510,6 @@ static bool CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id) {
if (obj->as<NativeObject>().contains(cx, id)) {
return false;
}
} else if (obj->is<UnboxedPlainObject>()) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx,
id)) {
return false;
}
} else if (obj->is<TypedObject>()) {
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
return false;
@ -674,23 +660,10 @@ static void GuardGroupProto(CacheIRWriter& writer, JSObject* obj,
}
// Guard that a given object has same class and same OwnProperties (excluding
// dense elements and dynamic properties). Returns an OperandId for the unboxed
// expando if it exists.
// dense elements and dynamic properties).
static void TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj,
ObjOperandId objId,
Maybe<ObjOperandId>* expandoId) {
if (obj->is<UnboxedPlainObject>()) {
writer.guardGroupForLayout(objId, obj->group());
if (UnboxedExpandoObject* expando =
obj->as<UnboxedPlainObject>().maybeExpando()) {
expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
writer.guardShapeForOwnProperties(expandoId->ref(),
expando->lastProperty());
} else {
writer.guardNoUnboxedExpando(objId);
}
} else if (obj->is<TypedObject>()) {
ObjOperandId objId) {
if (obj->is<TypedObject>()) {
writer.guardGroupForLayout(objId, obj->group());
} else if (obj->is<ProxyObject>()) {
writer.guardShapeForClass(objId, obj->as<ProxyObject>().shape());
@ -728,9 +701,7 @@ static void GeneratePrototypeGuardsForReceiver(CacheIRWriter& writer,
#ifdef DEBUG
// The following cases already guaranteed the prototype is unchanged.
if (obj->is<UnboxedPlainObject>()) {
MOZ_ASSERT(!obj->group()->hasUncacheableProto());
} else if (obj->is<TypedObject>()) {
if (obj->is<TypedObject>()) {
MOZ_ASSERT(!obj->group()->hasUncacheableProto());
} else if (obj->is<ProxyObject>()) {
MOZ_ASSERT(!obj->hasUncacheableProto());
@ -956,8 +927,7 @@ template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
static void EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj,
JSObject* holder, ObjOperandId objId,
Maybe<ObjOperandId>* holderId) {
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
TestMatchingReceiver(writer, obj, objId);
if (obj != holder) {
if (holder) {
@ -981,8 +951,6 @@ static void EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj,
// CanAttachNativeGetProp().
ShapeGuardProtoChain(writer, obj, objId);
}
} else if (obj->is<UnboxedPlainObject>()) {
holderId->emplace(*expandoId);
} else {
holderId->emplace(objId);
}
@ -996,10 +964,6 @@ static void EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj,
EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId,
&holderId);
if (obj == holder && obj->is<UnboxedPlainObject>()) {
holder = obj->as<UnboxedPlainObject>().maybeExpando();
}
// Slot access.
if (holder) {
MOZ_ASSERT(holderId->valid());
@ -1056,8 +1020,7 @@ static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj,
// is a Window as GuardHasGetterSetter doesn't support this yet (Window may
// require outerizing).
if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
TestMatchingReceiver(writer, obj, objId);
if (obj != holder) {
GeneratePrototypeGuards(writer, obj, holder, objId);
@ -1734,60 +1697,6 @@ bool GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId,
MOZ_CRASH("Unexpected ProxyStubType");
}
bool GetPropIRGenerator::tryAttachUnboxed(HandleObject obj, ObjOperandId objId,
HandleId id) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
const UnboxedLayout::Property* property =
obj->as<UnboxedPlainObject>().layout().lookup(id);
if (!property) {
return false;
}
maybeEmitIdGuard(id);
writer.guardGroupForLayout(objId, obj->group());
writer.loadUnboxedPropertyResult(
objId, property->type,
UnboxedPlainObject::offsetOfData() + property->offset);
if (property->type == JSVAL_TYPE_OBJECT) {
writer.typeMonitorResult();
} else {
writer.returnFromIC();
}
preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
trackAttached("Unboxed");
return true;
}
bool GetPropIRGenerator::tryAttachUnboxedExpando(HandleObject obj,
ObjOperandId objId,
HandleId id) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando) {
return false;
}
Shape* shape = expando->lookup(cx_, id);
if (!shape || !shape->isDataProperty()) {
return false;
}
maybeEmitIdGuard(id);
EmitReadSlotResult(writer, obj, obj, shape, objId);
EmitReadSlotReturn(writer, obj, obj, shape);
trackAttached("UnboxedExpando");
return true;
}
static TypedThingLayout GetTypedThingLayout(const Class* clasp) {
if (IsTypedArrayClass(clasp)) {
return Layout_TypedArray;
@ -2398,52 +2307,6 @@ bool GetPropIRGenerator::tryAttachTypedElement(HandleObject obj,
return true;
}
bool GetPropIRGenerator::tryAttachUnboxedElementHole(HandleObject obj,
ObjOperandId objId,
uint32_t index,
Int32OperandId indexId) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
// Only support unboxed objects with no elements (i.e. no expando)
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (expando) {
return false;
}
if (JSObject* proto = obj->staticPrototype()) {
// Start the check at the first object on the [[Prototype]],
// which must be native now.
if (!proto->isNative()) {
return false;
}
if (proto->as<NativeObject>().getDenseInitializedLength() != 0) {
return false;
}
if (!CanAttachDenseElementHole(&proto->as<NativeObject>(), false)) {
return false;
}
}
// Guard on the group and prevent expandos from appearing.
Maybe<ObjOperandId> tempId;
TestMatchingReceiver(writer, obj, objId, &tempId);
// Guard that the prototype chain has no elements.
GeneratePrototypeHoleGuards(writer, obj, objId,
/* alwaysGuardFirstProto = */ false);
writer.loadUndefinedResult();
// No monitor: We know undefined must be in the typeset already.
writer.returnFromIC();
trackAttached("UnboxedElementHole");
return true;
}
bool GetPropIRGenerator::tryAttachGenericElement(HandleObject obj,
ObjOperandId objId,
uint32_t index,
@ -3091,15 +2954,9 @@ bool HasPropIRGenerator::tryAttachNamedProp(HandleObject obj,
if (tryAttachNative(obj, objId, key, keyId, prop, holder)) {
return true;
}
if (tryAttachUnboxed(obj, objId, key, keyId)) {
return true;
}
if (tryAttachTypedObject(obj, objId, key, keyId)) {
return true;
}
if (tryAttachUnboxedExpando(obj, objId, key, keyId)) {
return true;
}
return false;
}
@ -3140,54 +2997,6 @@ bool HasPropIRGenerator::tryAttachNative(JSObject* obj, ObjOperandId objId,
return true;
}
bool HasPropIRGenerator::tryAttachUnboxed(JSObject* obj, ObjOperandId objId,
jsid key, ValOperandId keyId) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
const UnboxedLayout::Property* prop =
obj->as<UnboxedPlainObject>().layout().lookup(key);
if (!prop) {
return false;
}
emitIdGuard(keyId, key);
writer.guardGroupForLayout(objId, obj->group());
writer.loadBooleanResult(true);
writer.returnFromIC();
trackAttached("UnboxedHasProp");
return true;
}
bool HasPropIRGenerator::tryAttachUnboxedExpando(JSObject* obj,
ObjOperandId objId, jsid key,
ValOperandId keyId) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando) {
return false;
}
Shape* shape = expando->lookup(cx_, key);
if (!shape) {
return false;
}
Maybe<ObjOperandId> tempId;
emitIdGuard(keyId, key);
EmitReadSlotGuard(writer, obj, obj, objId, &tempId);
writer.loadBooleanResult(true);
writer.returnFromIC();
trackAttached("UnboxedExpandoHasProp");
return true;
}
bool HasPropIRGenerator::tryAttachTypedArray(HandleObject obj,
ObjOperandId objId,
Int32OperandId indexId) {
@ -3237,8 +3046,7 @@ bool HasPropIRGenerator::tryAttachSlotDoesNotExist(JSObject* obj,
emitIdGuard(keyId, key);
if (hasOwn) {
Maybe<ObjOperandId> tempId;
TestMatchingReceiver(writer, obj, objId, &tempId);
TestMatchingReceiver(writer, obj, objId);
} else {
Maybe<ObjOperandId> tempId;
EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
@ -3256,7 +3064,7 @@ bool HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj,
bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
// Check that property doesn't exist on |obj| or it's prototype chain. These
// checks allow Native/Unboxed/Typed objects with a NativeObject prototype
// checks allow Native/Typed objects with a NativeObject prototype
// chain. They return false if unknown such as resolve hooks or proxies.
if (hasOwn) {
if (!CheckHasNoSuchOwnProperty(cx_, obj, key)) {
@ -3456,12 +3264,6 @@ bool SetPropIRGenerator::tryAttachStub() {
if (tryAttachNativeSetSlot(obj, objId, id, rhsValId)) {
return true;
}
if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId)) {
return true;
}
if (tryAttachUnboxedProperty(obj, objId, id, rhsValId)) {
return true;
}
if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId)) {
return true;
}
@ -3617,79 +3419,6 @@ bool SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj,
return true;
}
bool SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj,
ObjOperandId objId,
HandleId id,
ValOperandId rhsId) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando) {
return false;
}
Shape* propShape = LookupShapeForSetSlot(JSOp(*pc_), expando, id);
if (!propShape) {
return false;
}
maybeEmitIdGuard(id);
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
// Property types must be added to the unboxed object's group, not the
// expando's group (it has unknown properties).
typeCheckInfo_.set(obj->group(), id);
EmitStoreSlotAndReturn(writer, expandoId.ref(), expando, propShape, rhsId);
trackAttached("UnboxedExpando");
return true;
}
static void EmitGuardUnboxedPropertyType(CacheIRWriter& writer,
JSValueType propType,
ValOperandId valId) {
if (propType == JSVAL_TYPE_OBJECT) {
// Unboxed objects store NullValue as nullptr object.
writer.guardIsObjectOrNull(valId);
} else {
MOZ_ASSERT(propType <= JSVAL_TYPE_OBJECT);
writer.guardType(valId, ValueType(propType));
}
}
bool SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj,
ObjOperandId objId,
HandleId id,
ValOperandId rhsId) {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
const UnboxedLayout::Property* property =
obj->as<UnboxedPlainObject>().layout().lookup(id);
if (!property) {
return false;
}
maybeEmitIdGuard(id);
writer.guardGroupForLayout(objId, obj->group());
EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
writer.storeUnboxedProperty(
objId, property->type,
UnboxedPlainObject::offsetOfData() + property->offset, rhsId);
writer.returnFromIC();
typeCheckInfo_.set(obj->group(), id);
preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
trackAttached("Unboxed");
return true;
}
bool SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj,
ObjOperandId objId,
HandleId id,
@ -3915,8 +3644,7 @@ bool SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId,
// is a Window as GuardHasGetterSetter doesn't support this yet (Window may
// require outerizing).
if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
TestMatchingReceiver(writer, obj, objId);
if (obj != holder) {
GeneratePrototypeGuards(writer, obj, holder, objId);
@ -4639,32 +4367,17 @@ bool SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup,
return false;
}
Shape* propShape = nullptr;
NativeObject* holderOrExpando = nullptr;
if (obj->isNative()) {
propShape = prop.shape();
holderOrExpando = &obj->as<NativeObject>();
} else {
if (!obj->is<UnboxedPlainObject>()) {
return false;
}
UnboxedExpandoObject* expando =
obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando) {
return false;
}
propShape = expando->lookupPure(id);
if (!propShape) {
return false;
}
holderOrExpando = expando;
if (!obj->isNative()) {
return false;
}
Shape* propShape = prop.shape();
NativeObject* holder = &obj->as<NativeObject>();
MOZ_ASSERT(propShape);
// The property must be the last added property of the object.
MOZ_RELEASE_ASSERT(holderOrExpando->lastProperty() == propShape);
MOZ_RELEASE_ASSERT(holder->lastProperty() == propShape);
// Old shape should be parent of new shape. Object flag updates may make this
// false even for simple data properties. It may be possible to support these
@ -4705,31 +4418,25 @@ bool SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup,
// Shape guard the holder.
ObjOperandId holderId = objId;
if (!obj->isNative()) {
MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
holderId = writer.guardAndLoadUnboxedExpando(objId);
}
writer.guardShape(holderId, oldShape);
ShapeGuardProtoChain(writer, obj, objId);
ObjectGroup* newGroup = obj->group();
// Check if we have to change the object's group. If we're adding an
// unboxed expando property, we pass the expando object to AddAndStore*Slot.
// That's okay because we only have to do a group change if the object is a
// PlainObject.
// Check if we have to change the object's group. We only have to change from
// a partially to fully initialized group if the object is a PlainObject.
bool changeGroup = oldGroup != newGroup;
MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
if (holderOrExpando->isFixedSlot(propShape->slot())) {
if (holder->isFixedSlot(propShape->slot())) {
size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
changeGroup, newGroup);
trackAttached("AddSlot");
} else {
size_t offset =
holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
holder->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
if (numOldSlots == numNewSlots) {
@ -4939,18 +4646,13 @@ bool GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId,
return false;
}
MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
MOZ_ASSERT(obj->isNative());
// Guard on the receiver's shape/group.
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
// Guard on the receiver's shape.
TestMatchingNativeReceiver(writer, &obj->as<NativeObject>(), objId);
// Ensure the receiver or its expando object has no dense elements.
if (obj->isNative()) {
writer.guardNoDenseElements(objId);
} else if (expandoId) {
writer.guardNoDenseElements(*expandoId);
}
// Ensure the receiver has no dense elements.
writer.guardNoDenseElements(objId);
// Do the same for the objects on the proto chain.
GeneratePrototypeHoleGuards(writer, obj, objId,
@ -5341,7 +5043,7 @@ bool CallIRGenerator::getTemplateObjectForScripted(HandleFunction calleeFunc,
MOZ_ASSERT(thisObject->nonCCWRealm() == calleeFunc->realm());
if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>()) {
if (thisObject->is<PlainObject>()) {
result.set(thisObject);
}
@ -6710,8 +6412,7 @@ void NewObjectIRGenerator::trackAttached(const char* name) {
bool NewObjectIRGenerator::tryAttachStub() {
AutoAssertNoPendingException aanpe(cx_);
if (!templateObject_->is<UnboxedPlainObject>() &&
templateObject_->as<PlainObject>().hasDynamicSlots()) {
if (templateObject_->as<PlainObject>().hasDynamicSlots()) {
trackAttached(IRGenerator::NotAttached);
return false;
}

View File

@ -232,8 +232,6 @@ extern const uint32_t ArgLengths[];
_(GuardMagicValue, Id, Byte) \
_(GuardFrameHasNoArgumentsObject, None) \
_(GuardNoDenseElements, Id) \
_(GuardNoUnboxedExpando, Id) \
_(GuardAndLoadUnboxedExpando, Id, Id) \
_(GuardAndGetIndexFromString, Id, Id) \
_(GuardAndGetNumberFromString, Id, Id) \
_(GuardAndGetIterator, Id, Id, Field, Field) \
@ -279,7 +277,6 @@ extern const uint32_t ArgLengths[];
_(AllocateAndStoreDynamicSlot, Id, Field, Id, Byte, Field, Field, Field) \
_(StoreTypedObjectReferenceProperty, Id, Field, Byte, Byte, Id) \
_(StoreTypedObjectScalarProperty, Id, Field, Byte, Byte, Id) \
_(StoreUnboxedProperty, Id, Byte, Field, Id) \
_(StoreDenseElement, Id, Id, Id) \
_(StoreDenseElementHole, Id, Id, Id, Byte) \
_(ArrayPush, Id, Id) \
@ -303,7 +300,6 @@ extern const uint32_t ArgLengths[];
/* The *Result ops load a value into the cache's result register. */ \
_(LoadFixedSlotResult, Id, Field) \
_(LoadDynamicSlotResult, Id, Field) \
_(LoadUnboxedPropertyResult, Id, Byte, Field) \
_(LoadTypedObjectResult, Id, Byte, Byte, Field) \
_(LoadDenseElementResult, Id, Id) \
_(LoadDenseElementHoleResult, Id, Id) \
@ -770,8 +766,7 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
void guardGroupForLayout(ObjOperandId obj, ObjectGroup* group) {
// NOTE: Comment in guardGroupForTypeBarrier also applies.
MOZ_ASSERT(!group->hasUncacheableClass());
MOZ_ASSERT(IsUnboxedObjectClass(group->clasp()) ||
IsTypedObjectClass(group->clasp()));
MOZ_ASSERT(IsTypedObjectClass(group->clasp()));
guardGroup(obj, group);
}
void guardProto(ObjOperandId obj, JSObject* proto) {
@ -918,15 +913,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
void guardNoDenseElements(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::GuardNoDenseElements, obj);
}
void guardNoUnboxedExpando(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
}
ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) {
ObjOperandId res(nextOperandId_++);
writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj);
writeOperandId(res);
return res;
}
ValOperandId loadStackValue(uint32_t idx) {
ValOperandId res(nextOperandId_++);
@ -1064,13 +1050,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
buffer_.writeByte(uint32_t(type));
writeOperandId(rhs);
}
void storeUnboxedProperty(ObjOperandId obj, JSValueType type, size_t offset,
ValOperandId rhs) {
writeOpWithOperandId(CacheOp::StoreUnboxedProperty, obj);
buffer_.writeByte(uint32_t(type));
addStubField(offset, StubField::Type::RawWord);
writeOperandId(rhs);
}
void storeDenseElement(ObjOperandId obj, Int32OperandId index,
ValOperandId rhs) {
writeOpWithOperandId(CacheOp::StoreDenseElement, obj);
@ -1377,12 +1356,6 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
addStubField(offset, StubField::Type::RawWord);
}
void loadUnboxedPropertyResult(ObjOperandId obj, JSValueType type,
size_t offset) {
writeOpWithOperandId(CacheOp::LoadUnboxedPropertyResult, obj);
buffer_.writeByte(uint32_t(type));
addStubField(offset, StubField::Type::RawWord);
}
void loadTypedObjectResult(ObjOperandId obj, uint32_t offset,
TypedThingLayout layout, uint32_t typeDescr) {
MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX);
@ -1806,8 +1779,6 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator {
uint32_t index, Int32OperandId indexId);
bool tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
uint32_t index, Int32OperandId indexId);
bool tryAttachUnboxedElementHole(HandleObject obj, ObjOperandId objId,
uint32_t index, Int32OperandId indexId);
bool tryAttachGenericElement(HandleObject obj, ObjOperandId objId,
uint32_t index, Int32OperandId indexId);

View File

@ -1815,37 +1815,6 @@ bool CacheIRCompiler::emitGuardMagicValue() {
return true;
}
bool CacheIRCompiler::emitGuardNoUnboxedExpando() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
Register obj = allocator.useRegister(masm, reader.objOperandId());
FailurePath* failure;
if (!addFailurePath(&failure)) {
return false;
}
Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
masm.branchPtr(Assembler::NotEqual, expandoAddr, ImmWord(0),
failure->label());
return true;
}
bool CacheIRCompiler::emitGuardAndLoadUnboxedExpando() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
Register obj = allocator.useRegister(masm, reader.objOperandId());
Register output = allocator.defineRegister(masm, reader.objOperandId());
FailurePath* failure;
if (!addFailurePath(&failure)) {
return false;
}
Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
masm.loadPtr(expandoAddr, output);
masm.branchTestPtr(Assembler::Zero, output, output, failure->label());
return true;
}
bool CacheIRCompiler::emitGuardNoDetachedTypedObjects() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
FailurePath* failure;

View File

@ -41,8 +41,6 @@ namespace jit {
_(GuardNotDOMProxy) \
_(GuardSpecificInt32Immediate) \
_(GuardMagicValue) \
_(GuardNoUnboxedExpando) \
_(GuardAndLoadUnboxedExpando) \
_(GuardNoDetachedTypedObjects) \
_(GuardNoDenseElements) \
_(GuardAndGetNumberFromString) \

View File

@ -1213,17 +1213,6 @@ bool IonCacheIRCompiler::emitCallNativeGetElementResult() {
return true;
}
bool IonCacheIRCompiler::emitLoadUnboxedPropertyResult() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
AutoOutputRegister output(*this);
Register obj = allocator.useRegister(masm, reader.objOperandId());
JSValueType fieldType = reader.jsValueType();
int32_t fieldOffset = int32StubField(reader.stubOffset());
masm.loadUnboxedProperty(Address(obj, fieldOffset), fieldType, output);
return true;
}
bool IonCacheIRCompiler::emitGuardFrameHasNoArgumentsObject() {
MOZ_CRASH("Baseline-specific op");
}
@ -1674,38 +1663,6 @@ bool IonCacheIRCompiler::emitAllocateAndStoreDynamicSlot() {
return emitAddAndStoreSlotShared(CacheOp::AllocateAndStoreDynamicSlot);
}
bool IonCacheIRCompiler::emitStoreUnboxedProperty() {
JitSpew(JitSpew_Codegen, __FUNCTION__);
Register obj = allocator.useRegister(masm, reader.objOperandId());
JSValueType fieldType = reader.jsValueType();
int32_t offset = int32StubField(reader.stubOffset());
ConstantOrRegister val =
allocator.useConstantOrRegister(masm, reader.valOperandId());
Maybe<AutoScratchRegister> scratch;
if (needsPostBarrier() && UnboxedTypeNeedsPostBarrier(fieldType)) {
scratch.emplace(allocator, masm);
}
if (fieldType == JSVAL_TYPE_OBJECT && typeCheckInfo_->isSet()) {
FailurePath* failure;
if (!addFailurePath(&failure)) {
return false;
}
EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_,
failure->label());
}
// Note that the storeUnboxedProperty call here is infallible, as the
// IR emitter is responsible for guarding on |val|'s type.
Address fieldAddr(obj, offset);
EmitICUnboxedPreBarrier(masm, fieldAddr, fieldType);
masm.storeUnboxedProperty(fieldAddr, fieldType, val, /* failure = */ nullptr);
if (needsPostBarrier() && UnboxedTypeNeedsPostBarrier(fieldType)) {
emitPostBarrierSlot(obj, val, scratch.ref());
}
return true;
}
bool IonCacheIRCompiler::emitStoreTypedObjectReferenceProperty() {
JitSpew(JitSpew_Codegen, __FUNCTION__);

View File

@ -505,266 +505,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType,
bool allowDouble,
Register temp, Label* fail);
template <typename T>
void MacroAssembler::loadUnboxedProperty(T address, JSValueType type,
TypedOrValueRegister output) {
switch (type) {
case JSVAL_TYPE_INT32: {
// Handle loading an int32 into a double reg.
if (output.type() == MIRType::Double) {
convertInt32ToDouble(address, output.typedReg().fpu());
break;
}
MOZ_FALLTHROUGH;
}
case JSVAL_TYPE_BOOLEAN:
case JSVAL_TYPE_STRING: {
Register outReg;
if (output.hasValue()) {
outReg = output.valueReg().scratchReg();
} else {
MOZ_ASSERT(output.type() == MIRTypeFromValueType(type));
outReg = output.typedReg().gpr();
}
switch (type) {
case JSVAL_TYPE_BOOLEAN:
load8ZeroExtend(address, outReg);
break;
case JSVAL_TYPE_INT32:
load32(address, outReg);
break;
case JSVAL_TYPE_STRING:
loadPtr(address, outReg);
break;
default:
MOZ_CRASH();
}
if (output.hasValue()) {
tagValue(type, outReg, output.valueReg());
}
break;
}
case JSVAL_TYPE_OBJECT:
if (output.hasValue()) {
Register scratch = output.valueReg().scratchReg();
loadPtr(address, scratch);
Label notNull, done;
branchPtr(Assembler::NotEqual, scratch, ImmWord(0), &notNull);
moveValue(NullValue(), output.valueReg());
jump(&done);
bind(&notNull);
tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg());
bind(&done);
} else {
// Reading null can't be possible here, as otherwise the result
// would be a value (either because null has been read before or
// because there is a barrier).
Register reg = output.typedReg().gpr();
loadPtr(address, reg);
#ifdef DEBUG
Label ok;
branchTestPtr(Assembler::NonZero, reg, reg, &ok);
assumeUnreachable("Null not possible");
bind(&ok);
#endif
}
break;
case JSVAL_TYPE_DOUBLE:
// Note: doubles in unboxed objects are not accessed through other
// views and do not need canonicalization.
if (output.hasValue()) {
loadValue(address, output.valueReg());
} else {
loadDouble(address, output.typedReg().fpu());
}
break;
default:
MOZ_CRASH();
}
}
template void MacroAssembler::loadUnboxedProperty(Address address,
JSValueType type,
TypedOrValueRegister output);
template void MacroAssembler::loadUnboxedProperty(BaseIndex address,
JSValueType type,
TypedOrValueRegister output);
static void StoreUnboxedFailure(MacroAssembler& masm, Label* failure) {
// Storing a value to an unboxed property is a fallible operation and
// the caller must provide a failure label if a particular unboxed store
// might fail. Sometimes, however, a store that cannot succeed (such as
// storing a string to an int32 property) will be marked as infallible.
// This can only happen if the code involved is unreachable.
if (failure) {
masm.jump(failure);
} else {
masm.assumeUnreachable("Incompatible write to unboxed property");
}
}
template <typename T>
void MacroAssembler::storeUnboxedProperty(T address, JSValueType type,
const ConstantOrRegister& value,
Label* failure) {
switch (type) {
case JSVAL_TYPE_BOOLEAN:
if (value.constant()) {
if (value.value().isBoolean()) {
store8(Imm32(value.value().toBoolean()), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType::Boolean) {
store8(value.reg().typedReg().gpr(), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else {
if (failure) {
branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(),
failure);
}
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1,
type);
}
break;
case JSVAL_TYPE_INT32:
if (value.constant()) {
if (value.value().isInt32()) {
store32(Imm32(value.value().toInt32()), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType::Int32) {
store32(value.reg().typedReg().gpr(), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else {
if (failure) {
branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure);
}
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4,
type);
}
break;
case JSVAL_TYPE_DOUBLE:
if (value.constant()) {
if (value.value().isNumber()) {
ScratchDoubleScope fpscratch(*this);
loadConstantDouble(value.value().toNumber(), fpscratch);
storeDouble(fpscratch, address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType::Int32) {
ScratchDoubleScope fpscratch(*this);
convertInt32ToDouble(value.reg().typedReg().gpr(), fpscratch);
storeDouble(fpscratch, address);
} else if (value.reg().type() == MIRType::Double) {
storeDouble(value.reg().typedReg().fpu(), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else {
ValueOperand reg = value.reg().valueReg();
Label notInt32, end;
branchTestInt32(Assembler::NotEqual, reg, &notInt32);
{
ScratchDoubleScope fpscratch(*this);
int32ValueToDouble(reg, fpscratch);
storeDouble(fpscratch, address);
}
jump(&end);
bind(&notInt32);
if (failure) {
branchTestDouble(Assembler::NotEqual, reg, failure);
}
storeValue(reg, address);
bind(&end);
}
break;
case JSVAL_TYPE_OBJECT:
if (value.constant()) {
if (value.value().isObjectOrNull()) {
storePtr(ImmGCPtr(value.value().toObjectOrNull()), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else if (value.reg().hasTyped()) {
MOZ_ASSERT(value.reg().type() != MIRType::Null);
if (value.reg().type() == MIRType::Object) {
storePtr(value.reg().typedReg().gpr(), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else {
if (failure) {
Label ok;
branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok);
branchTestObject(Assembler::NotEqual, value.reg().valueReg(),
failure);
bind(&ok);
}
storeUnboxedPayload(value.reg().valueReg(), address,
/* width = */ sizeof(uintptr_t), type);
}
break;
case JSVAL_TYPE_STRING:
if (value.constant()) {
if (value.value().isString()) {
storePtr(ImmGCPtr(value.value().toString()), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else if (value.reg().hasTyped()) {
if (value.reg().type() == MIRType::String) {
storePtr(value.reg().typedReg().gpr(), address);
} else {
StoreUnboxedFailure(*this, failure);
}
} else {
if (failure) {
branchTestString(Assembler::NotEqual, value.reg().valueReg(),
failure);
}
storeUnboxedPayload(value.reg().valueReg(), address,
/* width = */ sizeof(uintptr_t), type);
}
break;
default:
MOZ_CRASH();
}
}
template void MacroAssembler::storeUnboxedProperty(
Address address, JSValueType type, const ConstantOrRegister& value,
Label* failure);
template void MacroAssembler::storeUnboxedProperty(
BaseIndex address, JSValueType type, const ConstantOrRegister& value,
Label* failure);
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
// and bails for anything that cannot be handled with our jit allocators.
void MacroAssembler::checkAllocatorState(Label* fail) {
@ -1447,13 +1187,6 @@ void MacroAssembler::initGCThing(Register obj, Register temp,
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
offset += sizeof(uintptr_t);
}
} else if (templateObj.isUnboxedPlainObject()) {
MOZ_ASSERT(!templateObj.unboxedObjectHasExpando());
storePtr(ImmPtr(nullptr),
Address(obj, UnboxedPlainObject::offsetOfExpando()));
if (initContents) {
initUnboxedObjectContents(obj, templateObj.unboxedObjectLayout());
}
} else {
MOZ_CRASH("Unknown object");
}
@ -1474,25 +1207,6 @@ void MacroAssembler::initGCThing(Register obj, Register temp,
#endif
}
void MacroAssembler::initUnboxedObjectContents(Register object,
const UnboxedLayout& layout) {
// Initialize reference fields of the object, per UnboxedPlainObject::create.
if (const int32_t* list = layout.traceList()) {
while (*list != -1) {
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
Address(object, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
list++;
while (*list != -1) {
storePtr(ImmWord(0),
Address(object, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
// Unboxed objects don't have Values to initialize.
MOZ_ASSERT(*(list + 1) == -1);
}
}
void MacroAssembler::compareStrings(JSOp op, Register left, Register right,
Register result, Label* fail) {

View File

@ -39,7 +39,6 @@
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/TypedArrayObject.h"
#include "vm/UnboxedObject.h"
// [SMDOC] MacroAssembler multi-platform overview
//
@ -2710,18 +2709,6 @@ class MacroAssembler : public MacroAssemblerSpecific {
void memoryBarrierBefore(const Synchronization& sync);
void memoryBarrierAfter(const Synchronization& sync);
// Load a property from an UnboxedPlainObject or UnboxedArrayObject.
template <typename T>
void loadUnboxedProperty(T address, JSValueType type,
TypedOrValueRegister output);
// Store a property to an UnboxedPlainObject, without triggering barriers.
// If failure is null, the value definitely has a type suitable for storing
// in the property.
template <typename T>
void storeUnboxedProperty(T address, JSValueType type,
const ConstantOrRegister& value, Label* failure);
void debugAssertIsObject(const ValueOperand& val);
void debugAssertObjHasFixedSlots(Register obj, Register scratch);
@ -2826,8 +2813,6 @@ class MacroAssembler : public MacroAssemblerSpecific {
TypedArrayObject* templateObj,
TypedArrayLength lengthKind);
void initUnboxedObjectContents(Register object, const UnboxedLayout& layout);
void newGCString(Register result, Register temp, Label* fail,
bool attemptNursery);
void newGCFatInlineString(Register result, Register temp, Label* fail,

View File

@ -42,10 +42,6 @@ inline bool TemplateObject::isInlineTypedObject() const {
return obj_->is<InlineTypedObject>();
}
inline bool TemplateObject::isUnboxedPlainObject() const {
return obj_->is<UnboxedPlainObject>();
}
inline bool TemplateObject::isCallObject() const {
return obj_->is<CallObject>();
}
@ -77,16 +73,6 @@ inline uint8_t* TemplateObject::getInlineTypedObjectMem(
return obj_->as<InlineTypedObject>().inlineTypedMem(nogc);
}
inline const UnboxedLayout& TemplateObject::unboxedObjectLayout() const {
return obj_->as<UnboxedPlainObject>().layoutDontCheckGeneration();
}
#ifdef DEBUG
inline bool TemplateObject::unboxedObjectHasExpando() const {
return obj_->as<UnboxedPlainObject>().maybeExpando();
}
#endif
inline const NativeTemplateObject& TemplateObject::asNativeTemplateObject()
const {
MOZ_ASSERT(isNative());

View File

@ -9,7 +9,6 @@
#include "vm/NativeObject.h"
#include "vm/Shape.h"
#include "vm/UnboxedObject.h"
namespace js {
namespace jit {
@ -43,7 +42,6 @@ class TemplateObject {
inline bool isTypedArrayObject() const;
inline bool isRegExpObject() const;
inline bool isInlineTypedObject() const;
inline bool isUnboxedPlainObject() const;
inline bool isCallObject() const;
inline bool isPlainObject() const;
@ -52,15 +50,10 @@ class TemplateObject {
inline gc::Cell* group() const;
inline gc::Cell* maybeShape() const;
// Some TypedObject and UnboxedPlainObject methods that can be called
// off-thread.
// Some TypedObjec methods that can be called off-thread.
inline uint32_t getInlineTypedObjectSize() const;
inline uint8_t* getInlineTypedObjectMem(
const JS::AutoRequireNoGC& nogc) const;
inline const UnboxedLayout& unboxedObjectLayout() const;
#ifdef DEBUG
inline bool unboxedObjectHasExpando() const;
#endif
};
class NativeTemplateObject : public TemplateObject {

View File

@ -244,8 +244,6 @@ namespace jit {
_(ToObjectSlow, js::ToObjectSlow) \
_(ToStringSlow, js::ToStringSlow<CanGC>) \
_(TrySkipAwait, js::jit::TrySkipAwait) \
_(UnboxedPlainObjectConvertToNative, \
js::UnboxedPlainObject::convertToNative) \
_(UrshValues, js::UrshValues)
// The list below is for tail calls. The third argument specifies the number of

View File

@ -30,7 +30,6 @@
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypeInference-inl.h"
#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@ -1764,12 +1763,6 @@ bool HasNativeDataPropertyPure(JSContext* cx, JSObject* obj, Value* vp) {
ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
return false;
}
} else if (obj->is<UnboxedPlainObject>()) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx,
id)) {
vp[1].setBoolean(true);
return true;
}
} else if (obj->is<TypedObject>()) {
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
vp[1].setBoolean(true);

View File

@ -68,271 +68,7 @@ static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
/* static */
bool UnboxedLayout::makeConstructorCode(JSContext* cx,
HandleObjectGroup group) {
gc::AutoSuppressGC suppress(cx);
using namespace jit;
if (!cx->realm()->ensureJitRealmExists(cx)) {
return false;
}
AutoSweepObjectGroup sweep(group);
UnboxedLayout& layout = group->unboxedLayout(sweep);
MOZ_ASSERT(!layout.constructorCode());
UnboxedPlainObject* templateObject =
UnboxedPlainObject::create(cx, group, TenuredObject);
if (!templateObject) {
return false;
}
JitContext jitContext(cx, nullptr);
StackMacroAssembler masm;
Register propertiesReg, newKindReg;
#ifdef JS_CODEGEN_X86
propertiesReg = eax;
newKindReg = ecx;
masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg);
masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg);
#else
propertiesReg = IntArgReg0;
newKindReg = IntArgReg1;
#endif
#ifdef JS_CODEGEN_ARM64
// ARM64 communicates stack address via sp, but uses a pseudo-sp (PSP) for
// addressing. The register we use for PSP may however also be used by
// calling code, and it is nonvolatile, so save it. Do this as a special
// case first because the generic save/restore code needs the PSP to be
// initialized already.
MOZ_ASSERT(PseudoStackPointer64.Is(masm.GetStackPointer64()));
masm.Str(PseudoStackPointer64, vixl::MemOperand(sp, -16, vixl::PreIndex));
// Initialize the PSP from the SP.
masm.initPseudoStackPtr();
#endif
MOZ_ASSERT(propertiesReg.volatile_());
MOZ_ASSERT(newKindReg.volatile_());
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(propertiesReg);
regs.take(newKindReg);
Register object = regs.takeAny(), scratch1 = regs.takeAny(),
scratch2 = regs.takeAny();
LiveGeneralRegisterSet savedNonVolatileRegisters =
SavedNonVolatileRegisters(regs);
masm.PushRegsInMask(savedNonVolatileRegisters);
// The scratch double register might be used by MacroAssembler methods.
if (ScratchDoubleReg.volatile_()) {
masm.push(ScratchDoubleReg);
}
Label failure, tenuredObject, allocated, unknownProperties;
masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject),
&tenuredObject);
masm.load32(AbsoluteAddress(group->addressOfFlags()), scratch1);
masm.branchTest32(Assembler::NonZero, scratch1,
Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &unknownProperties);
masm.branchTest32(Assembler::NonZero, scratch1, Imm32(OBJECT_FLAG_PRE_TENURE),
&tenuredObject);
masm.bind(&unknownProperties);
// Allocate an object in the nursery
TemplateObject templateObj(templateObject);
masm.createGCObject(object, scratch1, templateObj, gc::DefaultHeap, &failure,
/* initFixedSlots = */ false);
masm.jump(&allocated);
masm.bind(&tenuredObject);
// Allocate an object in the tenured heap.
masm.createGCObject(object, scratch1, templateObj, gc::TenuredHeap, &failure,
/* initFixedSlots = */ false);
// If any of the properties being stored are in the nursery, add a store
// buffer entry for the new object.
Label postBarrier;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property& property = layout.properties()[i];
if (!UnboxedTypeNeedsPostBarrier(property.type)) {
continue;
}
Address valueAddress(
propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
if (property.type == JSVAL_TYPE_OBJECT) {
Label notObject;
masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
Register valueObject = masm.extractObject(valueAddress, scratch1);
masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2,
&postBarrier);
masm.bind(&notObject);
} else {
MOZ_ASSERT(property.type == JSVAL_TYPE_STRING);
Label notString;
masm.branchTestString(Assembler::NotEqual, valueAddress, &notString);
masm.unboxString(valueAddress, scratch1);
masm.branchPtrInNurseryChunk(Assembler::Equal, scratch1, scratch2,
&postBarrier);
masm.bind(&notString);
}
}
masm.jump(&allocated);
masm.bind(&postBarrier);
LiveGeneralRegisterSet liveVolatileRegisters;
liveVolatileRegisters.add(propertiesReg);
if (object.volatile_()) {
liveVolatileRegisters.add(object);
}
masm.PushRegsInMask(liveVolatileRegisters);
masm.mov(ImmPtr(cx->runtime()), scratch1);
masm.setupUnalignedABICall(scratch2);
masm.passABIArg(scratch1);
masm.passABIArg(object);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
masm.PopRegsInMask(liveVolatileRegisters);
masm.bind(&allocated);
ValueOperand valueOperand;
#ifdef JS_NUNBOX32
valueOperand = ValueOperand(scratch1, scratch2);
#else
valueOperand = ValueOperand(scratch1);
#endif
Label failureStoreOther, failureStoreObject;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property& property = layout.properties()[i];
Address valueAddress(
propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
Address targetAddress(object,
UnboxedPlainObject::offsetOfData() + property.offset);
masm.loadValue(valueAddress, valueOperand);
if (property.type == JSVAL_TYPE_OBJECT) {
HeapTypeSet* types =
group->maybeGetProperty(sweep, IdToTypeId(NameToId(property.name)));
Label notObject;
masm.branchTestObject(Assembler::NotEqual, valueOperand,
types->mightBeMIRType(MIRType::Null)
? &notObject
: &failureStoreObject);
Register payloadReg = masm.extractObject(valueOperand, scratch1);
if (!types->hasType(TypeSet::AnyObjectType())) {
Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
masm.guardObjectType(payloadReg, types, scratch, payloadReg,
&failureStoreObject);
}
masm.storeUnboxedProperty(
targetAddress, JSVAL_TYPE_OBJECT,
TypedOrValueRegister(MIRType::Object, AnyRegister(payloadReg)),
nullptr);
if (notObject.used()) {
Label done;
masm.jump(&done);
masm.bind(&notObject);
masm.branchTestNull(Assembler::NotEqual, valueOperand,
&failureStoreOther);
masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(),
nullptr);
masm.bind(&done);
}
} else {
masm.storeUnboxedProperty(targetAddress, property.type,
ConstantOrRegister(valueOperand),
&failureStoreOther);
}
}
Label done;
masm.bind(&done);
if (object != ReturnReg) {
masm.movePtr(object, ReturnReg);
}
// Restore non-volatile registers which were saved on entry.
if (ScratchDoubleReg.volatile_()) {
masm.pop(ScratchDoubleReg);
}
masm.PopRegsInMask(savedNonVolatileRegisters);
#ifdef JS_CODEGEN_ARM64
// Now restore the value that was in the PSP register on entry, and return.
// Obtain the correct SP from the PSP.
masm.Mov(sp, PseudoStackPointer64);
// Restore the saved value of the PSP register, this value is whatever the
// caller had saved in it, not any actual SP value, and it must not be
// overwritten subsequently.
masm.Ldr(PseudoStackPointer64, vixl::MemOperand(sp, 16, vixl::PostIndex));
// Perform a plain Ret(), as abiret() will move SP <- PSP and that is wrong.
masm.Ret(vixl::lr);
#else
masm.abiret();
#endif
masm.bind(&failureStoreOther);
// There was a failure while storing a value which cannot be stored at all
// in the unboxed object. Initialize the object so it is safe for GC and
// return null.
masm.initUnboxedObjectContents(object,
templateObject->layoutDontCheckGeneration());
masm.bind(&failure);
masm.movePtr(ImmWord(0), object);
masm.jump(&done);
masm.bind(&failureStoreObject);
// There was a failure while storing a value to an object slot of the
// unboxed object. If the value is storable, the failure occurred due to
// incomplete type information in the object, so return a token to trigger
// regeneration of the jitcode after a new object is created in the VM.
{
Label isObject;
masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
masm.bind(&isObject);
}
// Initialize the object so it is safe for GC.
masm.initUnboxedObjectContents(object,
templateObject->layoutDontCheckGeneration());
masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
masm.jump(&done);
Linker linker(masm, "UnboxedObject");
JitCode* code = linker.newCode(cx, CodeKind::Other);
if (!code) {
return false;
}
layout.setConstructorCode(code);
return true;
}
void UnboxedLayout::detachFromRealm() {