mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 09:15:35 +00:00
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:
parent
991590f001
commit
eb0619d3c4
@ -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();
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -41,8 +41,6 @@ namespace jit {
|
||||
_(GuardNotDOMProxy) \
|
||||
_(GuardSpecificInt32Immediate) \
|
||||
_(GuardMagicValue) \
|
||||
_(GuardNoUnboxedExpando) \
|
||||
_(GuardAndLoadUnboxedExpando) \
|
||||
_(GuardNoDetachedTypedObjects) \
|
||||
_(GuardNoDenseElements) \
|
||||
_(GuardAndGetNumberFromString) \
|
||||
|
@ -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__);
|
||||
|
@ -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), ¬Null);
|
||||
|
||||
moveValue(NullValue(), output.valueReg());
|
||||
jump(&done);
|
||||
|
||||
bind(¬Null);
|
||||
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, ¬Int32);
|
||||
{
|
||||
ScratchDoubleScope fpscratch(*this);
|
||||
int32ValueToDouble(reg, fpscratch);
|
||||
storeDouble(fpscratch, address);
|
||||
}
|
||||
jump(&end);
|
||||
bind(¬Int32);
|
||||
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) {
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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, ¬Object);
|
||||
Register valueObject = masm.extractObject(valueAddress, scratch1);
|
||||
masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2,
|
||||
&postBarrier);
|
||||
masm.bind(¬Object);
|
||||
} else {
|
||||
MOZ_ASSERT(property.type == JSVAL_TYPE_STRING);
|
||||
Label notString;
|
||||
masm.branchTestString(Assembler::NotEqual, valueAddress, ¬String);
|
||||
masm.unboxString(valueAddress, scratch1);
|
||||
masm.branchPtrInNurseryChunk(Assembler::Equal, scratch1, scratch2,
|
||||
&postBarrier);
|
||||
masm.bind(¬String);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
? ¬Object
|
||||
: &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(¬Object);
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user