Bug 1165392, Bug 1165463 - Various unboxed array fixes and optimizations, r=jandem.

This commit is contained in:
Brian Hackett 2015-05-26 16:29:19 -06:00
parent 3ce99ceded
commit c6e41638c6
18 changed files with 594 additions and 279 deletions

View File

@ -41,7 +41,8 @@ AllocateObjectBuffer(ExclusiveContext* cx, uint32_t count)
{
if (cx->isJSContext()) {
Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
return static_cast<T*>(nursery.allocateBuffer(cx->zone(), count * sizeof(T)));
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
return static_cast<T*>(nursery.allocateBuffer(cx->zone(), nbytes));
}
return cx->zone()->pod_malloc<T>(count);
}
@ -52,7 +53,8 @@ AllocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, uint32_t count)
{
if (cx->isJSContext()) {
Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
return static_cast<T*>(nursery.allocateBuffer(obj, count * sizeof(T)));
size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
return static_cast<T*>(nursery.allocateBuffer(obj, nbytes));
}
return obj->zone()->pod_malloc<T>(count);
}

View File

@ -37,6 +37,7 @@
#include "vm/Interpreter-inl.h"
#include "vm/ScopeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/UnboxedObject-inl.h"
using mozilla::BitwiseCast;
using mozilla::DebugOnly;
@ -5154,29 +5155,13 @@ RemoveExistingTypedArraySetElemStub(JSContext* cx, ICSetElem_Fallback* stub, Han
return false;
}
static size_t
SetElemObjectInitializedLength(JSObject *obj)
{
if (obj->isNative())
return obj->as<NativeObject>().getDenseInitializedLength();
return obj->as<UnboxedArrayObject>().initializedLength();
}
static size_t
SetElemObjectCapacity(JSObject *obj)
{
if (obj->isNative())
return obj->as<NativeObject>().getDenseCapacity();
return obj->as<UnboxedArrayObject>().capacity();
}
static bool
CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
bool* isAddingCaseOut, size_t* protoDepthOut)
{
uint32_t initLength = SetElemObjectInitializedLength(obj);
uint32_t capacity = SetElemObjectCapacity(obj);
uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
*isAddingCaseOut = false;
*protoDepthOut = 0;
@ -5263,9 +5248,9 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
// Check the old capacity
uint32_t oldCapacity = 0;
uint32_t oldInitLength = 0;
if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
oldCapacity = obj->as<NativeObject>().getDenseCapacity();
oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
if (index.isInt32() && index.toInt32() >= 0) {
oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
}
if (op == JSOP_INITELEM) {
@ -5533,8 +5518,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
// Unbox key.
Register key = masm.extractInt32(R1, ExtractTemp1);
Address valueAddr(BaselineStackReg, ICStackValueOffset);
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
// Set element on a native object.
@ -5569,6 +5552,8 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
regs.takeUnchecked(obj);
regs.takeUnchecked(key);
Address valueAddr(BaselineStackReg, ICStackValueOffset);
// We need to convert int32 values being stored into doubles. In this case
// the heap typeset is guaranteed to contain both int32 and double, so it's
// okay to store a double. Note that double arrays are only created by
@ -5598,10 +5583,11 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
// Compute the address being written to.
BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
masm.Push(R0);
masm.loadValue(valueAddr, R0);
masm.storeUnboxedProperty(address, unboxedType_,
@ -5748,8 +5734,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
// Unbox key.
Register key = masm.extractInt32(R1, ExtractTemp1);
Address valueAddr(BaselineStackReg, ICStackValueOffset);
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
// Adding element to a native object.
@ -5793,6 +5777,9 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
masm.branchTest32(Assembler::Zero, elementsFlags,
Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
&dontConvertDoubles);
Address valueAddr(BaselineStackReg, ICStackValueOffset);
// Note that double arrays are only created by IonMonkey, so if we have no
// floating-point support Ion is disabled and there should be no double arrays.
if (cx->runtime()->jitSupportsFloatingPoint)
@ -5815,31 +5802,31 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
// Capacity check.
masm.checkUnboxedArrayCapacity(obj, Int32Key(key), scratchReg, &failure);
// Increment initLength before write.
masm.add32(Imm32(1), initLengthAddr);
// If length is now <= key, increment length before write.
Label skipIncrementLength;
masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
masm.add32(Imm32(1), lengthAddr);
masm.bind(&skipIncrementLength);
// Load obj->elements.
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
// Write the value first, since this can fail. No need for pre-barrier
// since we're not overwriting an old value.
masm.Push(R0);
Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
masm.loadValue(valueAddr, R0);
// Write the value. No need for pre-barrier since we're not overwriting an old value.
BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
masm.storeUnboxedProperty(address, unboxedType_,
ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
masm.Pop(R0);
// Increment initialized length.
masm.add32(Imm32(1), initLengthAddr);
// If length is now <= key, increment length.
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
Label skipIncrementLength;
masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
masm.add32(Imm32(1), lengthAddr);
masm.bind(&skipIncrementLength);
}
EmitReturnFromIC(masm);
@ -9956,18 +9943,11 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
}
if (native == js::array_concat) {
if (args.thisv().isObject() &&
args.thisv().toObject().is<ArrayObject>() &&
!args.thisv().toObject().isSingleton() &&
!args.thisv().toObject().group()->hasUnanalyzedPreliminaryObjects())
{
RootedObject proto(cx, args.thisv().toObject().getProto());
res.set(NewDenseEmptyArray(cx, proto, TenuredObject));
if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) {
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
TenuredObject, /* forceAnalyze = */ true));
if (!res)
return false;
res->setGroup(args.thisv().toObject().group());
return true;
}
}

View File

@ -2492,6 +2492,12 @@ CodeGenerator::visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir)
OutOfLineCode* ool = oolCallVM(CopyElementsForWriteInfo, lir,
(ArgList(), object), StoreNothing());
if (lir->mir()->checkNative()) {
masm.loadObjClass(object, temp);
masm.branchTest32(Assembler::NonZero, Address(temp, Class::offsetOfFlags()),
Imm32(Class::NON_NATIVE), ool->rejoin());
}
masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
masm.branchTest32(Assembler::NonZero,
Address(temp, ObjectElements::offsetOfFlags()),
@ -6808,9 +6814,11 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
template <typename T>
static void
StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value)
StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value,
bool preBarrier)
{
masm.patchableCallPreBarrier(address, type);
if (preBarrier)
masm.patchableCallPreBarrier(address, type);
if (value->isConstant()) {
Value v = *value->toConstant();
if (v.isMarkable()) {
@ -6829,12 +6837,15 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
{
MIRType type;
int32_t offsetAdjustment;
bool preBarrier;
if (lir->mir()->isStoreUnboxedObjectOrNull()) {
type = MIRType_Object;
offsetAdjustment = lir->mir()->toStoreUnboxedObjectOrNull()->offsetAdjustment();
preBarrier = lir->mir()->toStoreUnboxedObjectOrNull()->preBarrier();
} else if (lir->mir()->isStoreUnboxedString()) {
type = MIRType_String;
offsetAdjustment = lir->mir()->toStoreUnboxedString()->offsetAdjustment();
preBarrier = lir->mir()->toStoreUnboxedString()->preBarrier();
} else {
MOZ_CRASH();
}
@ -6845,10 +6856,10 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
if (index->isConstant()) {
Address address(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment);
StoreUnboxedPointer(masm, address, type, value);
StoreUnboxedPointer(masm, address, type, value, preBarrier);
} else {
BaseIndex address(elements, ToRegister(index), ScalePointer, offsetAdjustment);
StoreUnboxedPointer(masm, address, type, value);
StoreUnboxedPointer(masm, address, type, value, preBarrier);
}
}
@ -6889,14 +6900,22 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
// VM call if a write barrier is necessary.
masm.branchTestNeedsIncrementalBarrier(Assembler::NonZero, ool->entry());
// Load elements and length.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
// VM call if length != initializedLength.
// Load elements and length, and VM call if length != initializedLength.
Int32Key key = Int32Key(lengthTemp);
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
} else {
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp);
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
}
// Test for length != 0. On zero length either take a VM call or generate
// an undefined value, depending on whether the call is known to produce
@ -6915,25 +6934,41 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
masm.bumpKey(&key, -1);
if (mir->mode() == MArrayPopShift::Pop) {
masm.loadElementTypedOrValue(BaseIndex(elementsTemp, lengthTemp, TimesEight), out,
mir->needsHoleCheck(), ool->entry());
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
} else {
size_t elemSize = UnboxedTypeSize(mir->unboxedType());
BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize));
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
}
} else {
MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
masm.loadElementTypedOrValue(Address(elementsTemp, 0), out, mir->needsHoleCheck(),
ool->entry());
Address addr(elementsTemp, 0);
if (mir->unboxedType() == JSVAL_TYPE_MAGIC)
masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
else
masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
}
// Handle the failure case when the array length is non-writable in the
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
// capacity <= length invariant for such arrays to avoid an explicit
// check.)
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
// Handle the failure case when the array length is non-writable in the
// OOL path. (Unlike in the adding-an-element cases, we can't rely on the
// capacity <= length invariant for such arrays to avoid an explicit
// check.)
Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
// Now adjust length and initializedLength.
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
// Now adjust length and initializedLength.
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
} else {
// Unboxed arrays always have writable lengths. Adjust length and
// initializedLength.
masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength()));
masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
}
if (mir->mode() == MArrayPopShift::Shift) {
// Don't save the temp registers.
@ -6972,7 +7007,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir)
emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
}
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*);
typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
static const VMFunction ArrayPushDenseInfo =
FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
@ -6982,25 +7017,51 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
{
OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
// Load elements and length.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
Int32Key key = Int32Key(length);
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
// Load elements and length.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
// Guard length == initializedLength.
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
// Guard length == initializedLength.
Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
// Guard length < capacity.
masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
// Guard length < capacity.
Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
// Do the store.
masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
} else {
// Load initialized length.
masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length);
// Guard length == initializedLength.
Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
// Guard length < capacity.
masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry());
// Load elements and do the store.
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
size_t elemSize = UnboxedTypeSize(mir->unboxedType());
BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize));
masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr);
}
masm.bumpKey(&key, 1);
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
// Update length and initialized length.
if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
} else {
masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength()));
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
}
masm.bind(ool->rejoin());
}
@ -7044,13 +7105,23 @@ CodeGenerator::visitArrayConcat(LArrayConcat* lir)
// inline and pass it to the stub. Else, we just pass nullptr and the stub falls
// back to a slow path.
Label fail, call;
masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
if (lir->mir()->unboxedType() == JSVAL_TYPE_MAGIC) {
masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
} else {
masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
}
// Try to allocate an object.
masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);

View File

@ -6619,7 +6619,7 @@ IonBuilder::jsop_initelem_array()
if (unboxedType != JSVAL_TYPE_MAGIC) {
// Note: storeUnboxedValue takes care of any post barriers on the value.
storeUnboxedValue(obj, elements, 0, id, unboxedType, value);
storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
current->add(increment);
@ -7101,6 +7101,7 @@ static bool
ClassHasEffectlessLookup(const Class* clasp)
{
return (clasp == &UnboxedPlainObject::class_) ||
(clasp == &UnboxedArrayObject::class_) ||
IsTypedObjectClass(clasp) ||
(clasp->isNative() && !clasp->ops.lookupProperty);
}
@ -9109,7 +9110,9 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
// Make sure the object being written to doesn't have copy on write elements.
object = addMaybeCopyElementsForWrite(object);
const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
bool checkNative = !clasp || !clasp->isNative();
object = addMaybeCopyElementsForWrite(object, checkNative);
if (NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(alloc(), object, value));
@ -9152,7 +9155,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
id = idInt32;
// Copy the elements vector if necessary.
obj = addMaybeCopyElementsForWrite(obj);
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
// Get the elements vector.
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
@ -11503,7 +11506,7 @@ IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType un
MInstruction*
IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
MDefinition* scaledOffset, JSValueType unboxedType,
MDefinition* value)
MDefinition* value, bool preBarrier /* = true */)
{
MInstruction* store;
switch (unboxedType) {
@ -11523,12 +11526,13 @@ IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t e
break;
case JSVAL_TYPE_STRING:
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset);
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
elementsOffset, preBarrier);
break;
case JSVAL_TYPE_OBJECT:
store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
elementsOffset);
elementsOffset, preBarrier);
break;
default:
@ -12533,11 +12537,11 @@ IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
}
MDefinition*
IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object)
IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
{
if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
return object;
MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object);
MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
current->add(copy);
return copy;
}

View File

@ -395,7 +395,7 @@ class IonBuilder
MDefinition* walkScopeChain(unsigned hops);
MInstruction* addConvertElementsToDoubles(MDefinition* elements);
MDefinition* addMaybeCopyElementsForWrite(MDefinition* object);
MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
@ -959,7 +959,7 @@ class IonBuilder
MInstruction* storeUnboxedValue(MDefinition* obj,
MDefinition* elements, int32_t elementsOffset,
MDefinition* scaledOffset, JSValueType unboxedType,
MDefinition* value);
MDefinition* value, bool preBarrier = true);
bool checkPreliminaryGroups(MDefinition *obj);
bool freezePropTypeSets(TemporaryTypeSet* types,
JSObject* foundProto, PropertyName* name);

View File

@ -628,6 +628,9 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
} else {
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
}
} else if (obj->is<UnboxedArrayObject>()) {
MOZ_ASSERT(failure);
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
} else if (obj->is<TypedObject>()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
@ -1154,6 +1157,40 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher&
return true;
}
static void
GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
JSObject* array, Register object, TypedOrValueRegister output)
{
Label failures;
Register outReg;
if (output.hasValue()) {
outReg = output.valueReg().scratchReg();
} else {
MOZ_ASSERT(output.type() == MIRType_Int32);
outReg = output.typedReg().gpr();
}
MOZ_ASSERT(object != outReg);
TestMatchingReceiver(masm, attacher, object, array, &failures);
// Load length.
masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg);
// Check for a length that fits in an int32.
masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
if (output.hasValue())
masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
// Success.
attacher.jumpRejoin(masm);
// Failure.
masm.bind(&failures);
attacher.jumpNextStub(masm);
}
// In this case, the code for TypedArray and SharedTypedArray is not the same,
// because the code embeds pointers to the respective class arrays. Code that
// caches the stub code must distinguish between the two cases.
@ -1244,7 +1281,7 @@ CanAttachNativeGetProp(JSContext* cx, const GetPropCache& cache,
// |length| is a non-configurable getter property on ArrayObjects. Any time this
// check would have passed, we can install a getter stub instead. Allow people to
// make that decision themselves with skipArrayLen
if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx, obj) &&
if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx) &&
IsCacheableArrayLength(cx, obj, name, cache.output()))
{
// The array length property is non-configurable, which means both that
@ -1277,7 +1314,7 @@ CanAttachNativeGetProp(JSContext* cx, const GetPropCache& cache,
}
bool
GetPropertyIC::allowArrayLength(JSContext* cx, HandleObject obj) const
GetPropertyIC::allowArrayLength(JSContext* cx) const
{
if (!idempotent())
return true;
@ -1402,6 +1439,33 @@ GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando");
}
bool
GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandlePropertyName name,
void* returnAddr, bool* emitted)
{
MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(outerScript->ionScript() == ion);
if (!obj->is<UnboxedArrayObject>())
return true;
if (cx->names().length != name)
return true;
if (obj->as<UnboxedArrayObject>().length() > INT32_MAX)
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output());
return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length");
}
bool
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandlePropertyName name, bool* emitted)
@ -1841,6 +1905,9 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, name, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
return false;
@ -3570,16 +3637,15 @@ GetElementIC::attachDenseElementHole(JSContext* cx, HandleScript outerScript, Io
}
/* static */ bool
GetElementIC::canAttachTypedArrayElement(JSObject* obj, const Value& idval,
TypedOrValueRegister output)
GetElementIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
TypedOrValueRegister output)
{
if (!IsAnyTypedArray(obj))
if (!IsAnyTypedArray(obj) && !obj->is<UnboxedArrayObject>())
return false;
if (!idval.isInt32() && !idval.isString())
return false;
// Don't emit a stub if the access is out of bounds. We make to make
// certain that we monitor the type coming out of the typed array when
// we generate the stub. Out of bounds accesses will hit the fallback
@ -3592,34 +3658,42 @@ GetElementIC::canAttachTypedArrayElement(JSObject* obj, const Value& idval,
if (index == UINT32_MAX)
return false;
}
if (index >= AnyTypedArrayLength(obj))
if (IsAnyTypedArray(obj)) {
if (index >= AnyTypedArrayLength(obj))
return false;
// The output register is not yet specialized as a float register, the only
// way to accept float typed arrays for now is to return a Value type.
uint32_t arrayType = AnyTypedArrayType(obj);
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
return output.hasValue();
return output.hasValue() || !output.typedReg().isFloat();
}
if (index >= obj->as<UnboxedArrayObject>().initializedLength())
return false;
// The output register is not yet specialized as a float register, the only
// way to accept float typed arrays for now is to return a Value type.
uint32_t arrayType = AnyTypedArrayType(obj);
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
if (elementType == JSVAL_TYPE_DOUBLE)
return output.hasValue();
return output.hasValue() || !output.typedReg().isFloat();
}
static void
GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
HandleObject tarr, const Value& idval, Register object,
ConstantOrRegister index, TypedOrValueRegister output,
bool allowDoubleResult)
GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
IonCache::StubAttacher& attacher,
HandleObject array, const Value& idval, Register object,
ConstantOrRegister index, TypedOrValueRegister output,
bool allowDoubleResult)
{
MOZ_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
MOZ_ASSERT(GetElementIC::canAttachTypedOrUnboxedArrayElement(array, idval, output));
Label failures;
// The array type is the object within the table of typed array classes.
Scalar::Type arrayType = AnyTypedArrayType(tarr);
// Guard on the shape.
Shape* shape = AnyTypedArrayShape(tarr);
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
TestMatchingReceiver(masm, attacher, object, array, &failures);
// Decide to what type index the stub should be optimized
Register tmpReg = output.scratchReg().gpr();
@ -3675,34 +3749,55 @@ GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
}
}
// Guard on the initialized length.
Address length(object, TypedArrayLayout::lengthOffset());
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
Label popObjectAndFail;
// Save the object register on the stack in case of failure.
Label popAndFail;
Register elementReg = object;
masm.push(object);
if (IsAnyTypedArray(array)) {
// Guard on the initialized length.
Address length(object, TypedArrayLayout::lengthOffset());
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
// Load elements vector.
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
// Save the object register on the stack in case of failure.
Register elementReg = object;
masm.push(object);
// Load the value. We use an invalid register because the destination
// register is necessary a non double register.
int width = Scalar::byteSize(arrayType);
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
if (output.hasValue()) {
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
elementReg, &popAndFail);
// Load elements vector.
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
// Load the value. We use an invalid register because the destination
// register is necessary a non double register.
Scalar::Type arrayType = AnyTypedArrayType(array);
int width = Scalar::byteSize(arrayType);
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
if (output.hasValue()) {
masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
elementReg, &popObjectAndFail);
} else {
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
}
} else {
masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popAndFail);
// Save the object register on the stack in case of failure.
masm.push(object);
// Guard on the initialized length.
masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object);
masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail);
// Load elements vector.
Register elementReg = object;
masm.loadPtr(Address(masm.getStackPointer(), 0), object);
masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg);
JSValueType elementType = array->as<UnboxedArrayObject>().elementType();
BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
masm.loadUnboxedProperty(source, elementType, output);
}
masm.pop(object);
attacher.jumpRejoin(masm);
// Restore the object before continuing to the next stub.
masm.bind(&popAndFail);
masm.bind(&popObjectAndFail);
masm.pop(object);
masm.bind(&failures);
@ -3710,13 +3805,14 @@ GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::Stub
}
bool
GetElementIC::attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject tarr, const Value& idval)
GetElementIC::attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript,
IonScript* ion, HandleObject tarr,
const Value& idval)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
allowDoubleResult());
GenerateGetTypedOrUnboxedArrayElement(cx, masm, attacher, tarr, idval, object(), index(),
output(), allowDoubleResult());
return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
}
@ -3887,8 +3983,8 @@ GetElementIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
return false;
attachedStub = true;
}
if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
if (!attachedStub && canAttachTypedOrUnboxedArrayElement(obj, idval, cache.output())) {
if (!cache.attachTypedOrUnboxedArrayElement(cx, outerScript, ion, obj, idval))
return false;
attachedStub = true;
}

View File

@ -467,7 +467,7 @@ class GetPropertyIC : public IonCache
};
// Helpers for CanAttachNativeGetProp
bool allowArrayLength(JSContext* cx, HandleObject obj) const;
bool allowArrayLength(JSContext* cx) const;
bool allowGetters() const {
return monitoredResult() && !idempotent();
}
@ -503,6 +503,10 @@ class GetPropertyIC : public IonCache
HandleObject obj, HandlePropertyName name,
void* returnAddr, bool* emitted);
bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandlePropertyName name,
void* returnAddr, bool* emitted);
bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandlePropertyName name, bool* emitted);
@ -668,7 +672,7 @@ class GetElementIC : public IonCache
// Helpers for CanAttachNativeGetProp
typedef JSContext * Context;
bool allowGetters() const { MOZ_ASSERT(!idempotent()); return true; }
bool allowArrayLength(Context, HandleObject) const { return false; }
bool allowArrayLength(Context) const { return false; }
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
return monitoredResult();
}
@ -677,8 +681,8 @@ class GetElementIC : public IonCache
static bool canAttachDenseElement(JSObject* obj, const Value& idval);
static bool canAttachDenseElementHole(JSObject* obj, const Value& idval,
TypedOrValueRegister output);
static bool canAttachTypedArrayElement(JSObject* obj, const Value& idval,
TypedOrValueRegister output);
static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
TypedOrValueRegister output);
bool attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, const Value& idval, HandlePropertyName name);
@ -689,8 +693,8 @@ class GetElementIC : public IonCache
bool attachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, const Value& idval);
bool attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject tarr, const Value& idval);
bool attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject tarr, const Value& idval);
bool attachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj);

View File

@ -4142,6 +4142,10 @@ class LMaybeCopyElementsForWrite : public LInstructionHelper<0, 1, 1>
const LDefinition* temp() {
return getTemp(0);
}
const MMaybeCopyElementsForWrite* mir() const {
return mir_->toMaybeCopyElementsForWrite();
}
};
// Load the initialized length from an elements header.

View File

@ -667,7 +667,10 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
MDefinition* obj = callInfo.thisArg();
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
if (!thisTypes)
return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
@ -679,9 +682,17 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
return InliningStatus_NotInlined;
}
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
if (clasp == &UnboxedArrayObject::class_) {
unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
if (unboxedType == JSVAL_TYPE_MAGIC)
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
obj = addMaybeCopyElementsForWrite(obj);
if (clasp == &ArrayObject::class_)
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
@ -692,7 +703,8 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
if (barrier != BarrierKind::NoBarrier)
returnType = MIRType_Value;
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
unboxedType, needsHoleCheck, maybeUndefined);
current->add(ins);
current->push(ins);
ins->setResultType(returnType);
@ -795,7 +807,10 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
return InliningStatus_NotInlined;
TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
if (!thisTypes)
return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW))
@ -816,6 +831,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
if (clasp == &UnboxedArrayObject::class_) {
unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
if (unboxedType == JSVAL_TYPE_MAGIC)
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
value = callInfo.getArg(0);
@ -827,12 +849,13 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
value = valueDouble;
}
obj = addMaybeCopyElementsForWrite(obj);
if (unboxedType == JSVAL_TYPE_MAGIC)
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
if (NeedsPostBarrier(info(), value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
MArrayPush* ins = MArrayPush::New(alloc(), obj, value);
MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
current->add(ins);
current->push(ins);
@ -863,7 +886,8 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
if (!thisTypes || !argTypes)
return InliningStatus_NotInlined;
if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
const Class* clasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW))
@ -872,7 +896,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_)
if (argTypes->getKnownClass(constraints()) != clasp)
return InliningStatus_NotInlined;
if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW))
@ -881,6 +905,15 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
if (clasp == &UnboxedArrayObject::class_) {
unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
if (unboxedType == JSVAL_TYPE_MAGIC)
return InliningStatus_NotInlined;
if (unboxedType != UnboxedArrayElementType(constraints(), callInfo.getArg(0), nullptr))
return InliningStatus_NotInlined;
}
// Watch out for indexed properties on the prototype.
if (ArrayPrototypeHasIndexedProperty(constraints(), script())) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
@ -934,13 +967,14 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
if (!templateObj || templateObj->group() != thisGroup)
return InliningStatus_NotInlined;
MOZ_ASSERT(templateObj->is<ArrayObject>());
callInfo.setImplicitlyUsedUnchecked();
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
&templateObj->as<ArrayObject>(),
templateObj->group()->initialHeap(constraints()));
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(),
callInfo.thisArg(), callInfo.getArg(0),
templateObj,
templateObj->group()->initialHeap(constraints()),
unboxedType);
current->add(ins);
current->push(ins);

View File

@ -360,6 +360,9 @@ class AliasSet {
MOZ_ASSERT(flags && !(flags & Store_));
return AliasSet(flags | Store_);
}
static uint32_t BoxedOrUnboxedElements(JSValueType type) {
return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
}
};
// An MDefinition is an SSA name.
@ -7758,8 +7761,10 @@ class MMaybeCopyElementsForWrite
: public MUnaryInstruction,
public SingleObjectPolicy::Data
{
explicit MMaybeCopyElementsForWrite(MDefinition* object)
: MUnaryInstruction(object)
bool checkNative_;
explicit MMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
: MUnaryInstruction(object), checkNative_(checkNative)
{
setGuard();
setMovable();
@ -7770,15 +7775,19 @@ class MMaybeCopyElementsForWrite
public:
INSTRUCTION_HEADER(MaybeCopyElementsForWrite)
static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object) {
return new(alloc) MMaybeCopyElementsForWrite(object);
static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object, bool checkNative) {
return new(alloc) MMaybeCopyElementsForWrite(object, checkNative);
}
MDefinition* object() const {
return getOperand(0);
}
bool checkNative() const {
return checkNative_;
}
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
return congruentIfOperandsEqual(ins) &&
checkNative() == ins->toMaybeCopyElementsForWrite()->checkNative();
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::ObjectFields);
@ -8542,9 +8551,7 @@ class MLoadElementHole
return congruentIfOperandsEqual(other);
}
AliasSet getAliasSet() const override {
return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC
? AliasSet::Element
: AliasSet::UnboxedElement);
return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType()));
}
void collectRangeInfoPreTrunc() override;
@ -8804,9 +8811,7 @@ class MStoreElementHole
// StoreElementHole can update the initialized length, the array length
// or reallocate obj->elements.
return AliasSet::Store(AliasSet::ObjectFields |
((unboxedType() == JSVAL_TYPE_MAGIC)
? AliasSet::Element
: AliasSet::UnboxedElement));
AliasSet::BoxedOrUnboxedElements(unboxedType()));
}
ALLOW_CLONE(MStoreElementHole)
@ -8818,11 +8823,12 @@ class MStoreUnboxedObjectOrNull
public StoreUnboxedObjectOrNullPolicy::Data
{
int32_t offsetAdjustment_;
bool preBarrier_;
MStoreUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
MDefinition* value, MDefinition* typedObj,
int32_t offsetAdjustment)
: offsetAdjustment_(offsetAdjustment)
int32_t offsetAdjustment, bool preBarrier)
: offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
{
initOperand(0, elements);
initOperand(1, index);
@ -8839,9 +8845,10 @@ class MStoreUnboxedObjectOrNull
static MStoreUnboxedObjectOrNull* New(TempAllocator& alloc,
MDefinition* elements, MDefinition* index,
MDefinition* value, MDefinition* typedObj,
int32_t offsetAdjustment = 0) {
int32_t offsetAdjustment = 0,
bool preBarrier = true) {
return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj,
offsetAdjustment);
offsetAdjustment, preBarrier);
}
MDefinition* elements() const {
return getOperand(0);
@ -8858,6 +8865,9 @@ class MStoreUnboxedObjectOrNull
int32_t offsetAdjustment() const {
return offsetAdjustment_;
}
bool preBarrier() const {
return preBarrier_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::UnboxedElement);
}
@ -8876,10 +8886,11 @@ class MStoreUnboxedString
public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
{
int32_t offsetAdjustment_;
bool preBarrier_;
MStoreUnboxedString(MDefinition* elements, MDefinition* index, MDefinition* value,
int32_t offsetAdjustment)
: offsetAdjustment_(offsetAdjustment)
int32_t offsetAdjustment, bool preBarrier)
: offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
{
initOperand(0, elements);
initOperand(1, index);
@ -8893,8 +8904,10 @@ class MStoreUnboxedString
static MStoreUnboxedString* New(TempAllocator& alloc,
MDefinition* elements, MDefinition* index,
MDefinition* value, int32_t offsetAdjustment = 0) {
return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment);
MDefinition* value, int32_t offsetAdjustment = 0,
bool preBarrier = true) {
return new(alloc) MStoreUnboxedString(elements, index, value,
offsetAdjustment, preBarrier);
}
MDefinition* elements() const {
return getOperand(0);
@ -8908,6 +8921,9 @@ class MStoreUnboxedString
int32_t offsetAdjustment() const {
return offsetAdjustment_;
}
bool preBarrier() const {
return preBarrier_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::UnboxedElement);
}
@ -8982,21 +8998,23 @@ class MArrayPopShift
private:
Mode mode_;
JSValueType unboxedType_;
bool needsHoleCheck_;
bool maybeUndefined_;
MArrayPopShift(MDefinition* object, Mode mode, bool needsHoleCheck, bool maybeUndefined)
: MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck),
maybeUndefined_(maybeUndefined)
MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType,
bool needsHoleCheck, bool maybeUndefined)
: MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType),
needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
{ }
public:
INSTRUCTION_HEADER(ArrayPopShift)
static MArrayPopShift* New(TempAllocator& alloc, MDefinition* object, Mode mode,
bool needsHoleCheck, bool maybeUndefined)
JSValueType unboxedType, bool needsHoleCheck, bool maybeUndefined)
{
return new(alloc) MArrayPopShift(object, mode, needsHoleCheck, maybeUndefined);
return new(alloc) MArrayPopShift(object, mode, unboxedType, needsHoleCheck, maybeUndefined);
}
MDefinition* object() const {
@ -9011,8 +9029,12 @@ class MArrayPopShift
bool mode() const {
return mode_;
}
JSValueType unboxedType() const {
return unboxedType_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
return AliasSet::Store(AliasSet::ObjectFields |
AliasSet::BoxedOrUnboxedElements(unboxedType()));
}
ALLOW_CLONE(MArrayPopShift)
@ -9023,8 +9045,10 @@ class MArrayPush
: public MBinaryInstruction,
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
{
MArrayPush(MDefinition* object, MDefinition* value)
: MBinaryInstruction(object, value)
JSValueType unboxedType_;
MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType)
: MBinaryInstruction(object, value), unboxedType_(unboxedType)
{
setResultType(MIRType_Int32);
}
@ -9032,8 +9056,9 @@ class MArrayPush
public:
INSTRUCTION_HEADER(ArrayPush)
static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value) {
return new(alloc) MArrayPush(object, value);
static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value,
JSValueType unboxedType) {
return new(alloc) MArrayPush(object, value, unboxedType);
}
MDefinition* object() const {
@ -9042,8 +9067,12 @@ class MArrayPush
MDefinition* value() const {
return getOperand(1);
}
JSValueType unboxedType() const {
return unboxedType_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
return AliasSet::Store(AliasSet::ObjectFields |
AliasSet::BoxedOrUnboxedElements(unboxedType()));
}
void computeRange(TempAllocator& alloc) override;
@ -9055,14 +9084,16 @@ class MArrayConcat
: public MBinaryInstruction,
public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
{
AlwaysTenured<ArrayObject*> templateObj_;
AlwaysTenuredObject templateObj_;
gc::InitialHeap initialHeap_;
JSValueType unboxedType_;
MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
ArrayObject* templateObj, gc::InitialHeap initialHeap)
JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
: MBinaryInstruction(lhs, rhs),
templateObj_(templateObj),
initialHeap_(initialHeap)
initialHeap_(initialHeap),
unboxedType_(unboxedType)
{
setResultType(MIRType_Object);
setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
@ -9073,12 +9104,14 @@ class MArrayConcat
static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
MDefinition* lhs, MDefinition* rhs,
ArrayObject* templateObj, gc::InitialHeap initialHeap)
JSObject* templateObj, gc::InitialHeap initialHeap,
JSValueType unboxedType)
{
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap);
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
initialHeap, unboxedType);
}
ArrayObject* templateObj() const {
JSObject* templateObj() const {
return templateObj_;
}
@ -9086,8 +9119,13 @@ class MArrayConcat
return initialHeap_;
}
JSValueType unboxedType() const {
return unboxedType_;
}
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
AliasSet::ObjectFields);
}
bool possiblyCalls() const override {
return true;

View File

@ -269,7 +269,7 @@ ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t delet
bool
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
MOZ_ASSERT(obj->is<ArrayObject>());
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
AutoDetectInvalidation adi(cx, rval);
@ -288,21 +288,14 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
}
bool
ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length)
ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
{
if (MOZ_LIKELY(obj->lengthIsWritable())) {
uint32_t idx = obj->length();
DenseElementResult result = obj->ensureDenseElements(cx, idx, 1);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Success) {
obj->setDenseElement(idx, v);
MOZ_ASSERT(idx < INT32_MAX);
*length = idx + 1;
obj->setLengthInt32(*length);
return true;
}
*length = GetAnyBoxedOrUnboxedArrayLength(obj);
DenseElementResult result =
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, DontUpdateTypes);
if (result != DenseElementResult::Incomplete) {
(*length)++;
return result == DenseElementResult::Success;
}
JS::AutoValueArray<3> argv(cx);
@ -319,7 +312,7 @@ ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* le
bool
ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
MOZ_ASSERT(obj->is<ArrayObject>());
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
AutoDetectInvalidation adi(cx, rval);
@ -340,21 +333,17 @@ ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
JSObject*
ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes)
{
Rooted<ArrayObject*> arr1(cx, &obj1->as<ArrayObject>());
Rooted<ArrayObject*> arr2(cx, &obj2->as<ArrayObject>());
Rooted<ArrayObject*> arrRes(cx, objRes ? &objRes->as<ArrayObject>() : nullptr);
if (arrRes) {
if (objRes) {
// Fast path if we managed to allocate an object inline.
if (!js::array_concat_dense(cx, arr1, arr2, arrRes))
if (!js::array_concat_dense(cx, obj1, obj2, objRes))
return nullptr;
return arrRes;
return objRes;
}
JS::AutoValueArray<3> argv(cx);
argv[0].setUndefined();
argv[1].setObject(*arr1);
argv[2].setObject(*arr2);
argv[1].setObject(*obj1);
argv[2].setObject(*obj2);
if (!js::array_concat(cx, 1, argv.begin()))
return nullptr;
return &argv[0].toObject();

View File

@ -658,7 +658,7 @@ template<bool Equal>
bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length);
bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
JSObject* ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject res);
JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);

View File

@ -2069,18 +2069,38 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp)
return SetLengthProperty(cx, obj, index);
}
void
js::ArrayShiftMoveElements(ArrayObject* obj)
template <JSValueType Type>
static inline DenseElementResult
ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
{
MOZ_ASSERT(obj->lengthIsWritable());
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
/*
* At this point the length and initialized length have already been
* decremented and the result fetched, so just shift the array elements
* themselves.
*/
uint32_t initlen = obj->getDenseInitializedLength();
obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
if (Type == JSVAL_TYPE_MAGIC) {
obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
} else {
uint8_t* data = obj->as<UnboxedArrayObject>().elements();
size_t elementSize = UnboxedTypeSize(Type);
memmove(data, data + elementSize, initlen * elementSize);
}
return DenseElementResult::Success;
}
DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
void
js::ArrayShiftMoveElements(JSObject* obj)
{
MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
}
template <JSValueType Type>
@ -2263,23 +2283,23 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
// object. The resulting array will have the same boxed/unboxed elements
// representation as the input object, and will either reuse the input
// object's group or will have unknown property types.
static inline JSObject*
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
JSObject*
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind, bool forceAnalyze)
{
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
return NewDenseFullyAllocatedArray(cx, length);
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
return NewDenseFullyAllocatedArray(cx, length);
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return nullptr;
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
NewObjectKind newKind = GenericObject;
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
@ -2442,7 +2462,7 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
if (!arr)
return false;
DebugOnly<DenseElementResult> result =
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, actualStart, actualDeleteCount);
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
MOZ_ASSERT(result.value == DenseElementResult::Success);
}
} else {
@ -2607,29 +2627,49 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
return true;
}
bool
js::array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
Handle<ArrayObject*> result)
template <JSValueType Type>
DenseElementResult
ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
{
uint32_t initlen1 = arr1->getDenseInitializedLength();
MOZ_ASSERT(initlen1 == arr1->length());
uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<Type>(obj1);
MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
uint32_t initlen2 = arr2->getDenseInitializedLength();
MOZ_ASSERT(initlen2 == arr2->length());
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<Type>(obj2);
MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
/* No overflow here due to nelements limit. */
uint32_t len = initlen1 + initlen2;
if (!result->ensureElements(cx, len))
return false;
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(result) == 0);
MOZ_ASSERT(!result->getDenseInitializedLength());
result->setDenseInitializedLength(len);
if (Type == JSVAL_TYPE_MAGIC) {
if (!result->as<ArrayObject>().ensureElements(cx, len))
return DenseElementResult::Failure;
} else {
if (result->as<UnboxedArrayObject>().capacity() < len) {
if (!result->as<UnboxedArrayObject>().growElements(cx, len))
return DenseElementResult::Failure;
}
}
result->initDenseElements(0, arr1->getDenseElements(), initlen1);
result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2);
result->setLengthInt32(len);
return true;
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj1, 0, 0, initlen1);
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj2, initlen1, 0, initlen2);
SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
return DenseElementResult::Success;
}
DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel,
JSContext*, JSObject*, JSObject*, JSObject*);
bool
js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
HandleObject result)
{
ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
MOZ_ASSERT(rv != DenseElementResult::Incomplete);
return rv == DenseElementResult::Success;
}
/*
@ -2661,7 +2701,7 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
DebugOnly<DenseElementResult> result =
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, initlen);
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
MOZ_ASSERT(result.value == DenseElementResult::Success);
args.rval().setObject(*narr);
@ -2917,7 +2957,7 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
if (count) {
DebugOnly<DenseElementResult> result =
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, begin, count);
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
MOZ_ASSERT(result.value == DenseElementResult::Success);
}
args.rval().setObject(*narr);

View File

@ -95,6 +95,12 @@ NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject
extern JSObject*
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
/* Create a dense or unboxed array, using the same group as |obj| if possible. */
extern JSObject*
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind = GenericObject,
bool forceAnalyze = false);
/*
* Determines whether a write to the given element on |obj| should fail because
* |obj| is an Array with a non-writable length, and writing that element would
@ -153,8 +159,8 @@ JSString*
ArrayJoin(JSContext* cx, HandleObject obj, HandleLinearString sepstr, uint32_t length);
extern bool
array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
Handle<ArrayObject*> result);
array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2,
HandleObject result);
bool
array_join(JSContext* cx, unsigned argc, js::Value* vp);
@ -163,7 +169,7 @@ extern JSString*
array_join_impl(JSContext* cx, HandleValue array, HandleString sep);
extern void
ArrayShiftMoveElements(ArrayObject* obj);
ArrayShiftMoveElements(JSObject* obj);
extern bool
array_shift(JSContext* cx, unsigned argc, js::Value* vp);

View File

@ -2810,6 +2810,12 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject**
MarkNonNativePropertyFound<NoGC>(propp);
return true;
}
} else if (obj->is<UnboxedArrayObject>()) {
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
*objp = obj;
MarkNonNativePropertyFound<NoGC>(propp);
return true;
}
} else if (obj->is<TypedObject>()) {
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
*objp = obj;

View File

@ -282,6 +282,16 @@ GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
return 0;
}
static inline size_t
GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
{
if (obj->isNative())
return obj->as<NativeObject>().getDenseCapacity();
if (obj->is<UnboxedArrayObject>())
return obj->as<UnboxedArrayObject>().capacity();
return 0;
}
static inline Value
GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
{
@ -290,6 +300,14 @@ GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
return obj->as<UnboxedArrayObject>().getElement(index);
}
static inline size_t
GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
{
if (obj->is<ArrayObject>())
return obj->as<ArrayObject>().length();
return obj->as<UnboxedArrayObject>().length();
}
static inline void
SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
{
@ -493,24 +511,26 @@ MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart,
template <JSValueType Type>
static inline DenseElementResult
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
uint32_t srcStart, uint32_t length)
uint32_t dstStart, uint32_t srcStart, uint32_t length)
{
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(src));
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(dst));
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == 0);
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == dstStart);
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<Type>(dst) >= length);
SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, length);
SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, dstStart + length);
if (Type == JSVAL_TYPE_MAGIC) {
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
dst->as<NativeObject>().initDenseElements(0, vp, length);
dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
} else {
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
size_t elementSize = UnboxedTypeSize(Type);
memcpy(dstData, srcData + srcStart * elementSize, length * elementSize);
memcpy(dstData + dstStart * elementSize,
srcData + srcStart * elementSize,
length * elementSize);
// Add a post barrier if we might have copied a nursery pointer to dst.
if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst))
@ -554,6 +574,18 @@ CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
#undef DEPENDENT_TEMPLATE_HINT
#define DefineBoxedOrUnboxedFunctor1(Signature, A) \
struct Signature ## Functor { \
A a; \
explicit Signature ## Functor(A a) \
: a(a) \
{} \
template <JSValueType Type> \
DenseElementResult operator()() { \
return Signature<Type>(a); \
} \
}
#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
struct Signature ## Functor { \
A a; B b; C c; \
@ -613,7 +645,7 @@ MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
DenseElementResult
CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
uint32_t srcStart, uint32_t length);
uint32_t dstStart, uint32_t srcStart, uint32_t length);
void
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);

View File

@ -1281,7 +1281,7 @@ UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
}
bool
UnboxedArrayObject::containsProperty(JSContext* cx, jsid id)
UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
{
if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
return true;
@ -1396,11 +1396,11 @@ UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id
if (!CanonicalizeArrayLengthValue(cx, v, &len))
return false;
UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
nobj->setLength(cx, len);
if (len < nobj->initializedLength()) {
nobj->setInitializedLength(len);
nobj->shrinkElements(cx, len);
}
nobj->setLength(cx, len);
return result.succeed();
}
@ -1439,6 +1439,15 @@ UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj
UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result)
{
if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
return result.succeed();
}
}
if (!convertToNative(cx, obj))
return false;
return DeleteProperty(cx, obj, id, result);
@ -1959,14 +1968,14 @@ js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
return CallBoxedOrUnboxedSpecialization(functor, obj);
}
DefineBoxedOrUnboxedFunctor5(CopyBoxedOrUnboxedDenseElements,
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t);
DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements,
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
DenseElementResult
js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
uint32_t srcStart, uint32_t length)
uint32_t dstStart, uint32_t srcStart, uint32_t length)
{
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, srcStart, length);
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
return CallBoxedOrUnboxedSpecialization(functor, dst);
}

View File

@ -435,7 +435,7 @@ class UnboxedArrayObject : public JSObject
return computeCapacity(capacityIndex(), length());
}
bool containsProperty(JSContext* cx, jsid id);
bool containsProperty(ExclusiveContext* cx, jsid id);
bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
bool initElement(ExclusiveContext* cx, size_t index, const Value& v);