Bug 1176451 - Optimize Array.concat when used on different types of boxed vs. unboxed arrays, r=jandem.

This commit is contained in:
Brian Hackett 2015-08-03 14:32:40 -07:00
Родитель fe87d76bdf
Коммит 0f1cdda9e1
7 изменённых файлов: 289 добавлений и 91 удалений

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

@ -9541,7 +9541,7 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
static bool static bool
GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
MutableHandleObject res) MutableHandleObject res, bool* skipAttach)
{ {
// Check for natives to which template objects can be attached. This is // Check for natives to which template objects can be attached. This is
// done to provide templates to Ion for inlining these natives later on. // done to provide templates to Ion for inlining these natives later on.
@ -9557,10 +9557,17 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
count = args[0].toInt32(); count = args[0].toInt32();
if (count <= ArrayObject::EagerAllocationMaxLength) { if (count <= ArrayObject::EagerAllocationMaxLength) {
ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
if (!group)
return false;
if (group->maybePreliminaryObjects()) {
*skipAttach = true;
return true;
}
// With this and other array templates, set forceAnalyze so that we // With this and other array templates, set forceAnalyze so that we
// don't end up with a template whose structure might change later. // don't end up with a template whose structure might change later.
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject, res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
/* forceAnalyze = */ true));
if (!res) if (!res)
return false; return false;
return true; return true;
@ -9568,17 +9575,30 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
} }
if (native == js::array_concat || native == js::array_slice) { if (native == js::array_concat || native == js::array_slice) {
if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) { if (args.thisv().isObject()) {
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0, JSObject* obj = &args.thisv().toObject();
TenuredObject, /* forceAnalyze = */ true)); if (!obj->isSingleton()) {
if (!res) if (obj->group()->maybePreliminaryObjects()) {
return false; *skipAttach = true;
return true;
}
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
TenuredObject));
return !!res;
}
} }
} }
if (native == js::str_split && args.length() == 1 && args[0].isString()) { if (native == js::str_split && args.length() == 1 && args[0].isString()) {
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject, ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
/* forceAnalyze = */ true)); if (!group)
return false;
if (group->maybePreliminaryObjects()) {
*skipAttach = true;
return true;
}
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
if (!res) if (!res)
return false; return false;
return true; return true;
@ -9877,9 +9897,15 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
RootedObject templateObject(cx); RootedObject templateObject(cx);
if (MOZ_LIKELY(!isSpread)) { if (MOZ_LIKELY(!isSpread)) {
bool skipAttach = false;
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject)) if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject, &skipAttach))
return false; return false;
if (skipAttach) {
*handled = true;
return true;
}
MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects());
} }
JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)", JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",

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

@ -7222,22 +7222,23 @@ CodeGenerator::visitArrayConcat(LArrayConcat* lir)
// inline and pass it to the stub. Else, we just pass nullptr and the stub falls // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
// back to a slow path. // back to a slow path.
Label fail, call; Label fail, call;
if (lir->mir()->unboxedType() == JSVAL_TYPE_MAGIC) { if (lir->mir()->unboxedThis()) {
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);
} else {
masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1); masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1); masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail); masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
} else {
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()->unboxedArg()) {
masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1); masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1); masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail); masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
} else {
masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
} }
// Try to allocate an object. // Try to allocate an object.

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

@ -864,9 +864,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
if (!thisTypes || !argTypes) if (!thisTypes || !argTypes)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints()); const Class* thisClasp = thisTypes->getKnownClass(constraints());
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_);
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW)) OBJECT_FLAG_LENGTH_OVERFLOW))
{ {
@ -874,8 +875,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
} }
if (argTypes->getKnownClass(constraints()) != clasp) const Class* argClasp = argTypes->getKnownClass(constraints());
if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
bool unboxedArg = (argClasp == &UnboxedArrayObject::class_);
if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
OBJECT_FLAG_LENGTH_OVERFLOW)) OBJECT_FLAG_LENGTH_OVERFLOW))
{ {
@ -883,15 +886,6 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
return InliningStatus_NotInlined; return InliningStatus_NotInlined;
} }
JSValueType unboxedType = JSVAL_TYPE_MAGIC;
if (clasp == &UnboxedArrayObject::class_) {
unboxedType = UnboxedArrayElementType(constraints(), thisArg, nullptr);
if (unboxedType == JSVAL_TYPE_MAGIC)
return InliningStatus_NotInlined;
if (unboxedType != UnboxedArrayElementType(constraints(), objArg, nullptr))
return InliningStatus_NotInlined;
}
// Watch out for indexed properties on the prototype. // Watch out for indexed properties on the prototype.
if (ArrayPrototypeHasIndexedProperty(this, script())) { if (ArrayPrototypeHasIndexedProperty(this, script())) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
@ -951,7 +945,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg, MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg,
templateObj, templateObj,
templateObj->group()->initialHeap(constraints()), templateObj->group()->initialHeap(constraints()),
unboxedType); unboxedThis, unboxedArg);
current->add(ins); current->add(ins);
current->push(ins); current->push(ins);

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

@ -9186,14 +9186,16 @@ class MArrayConcat
{ {
CompilerObject templateObj_; CompilerObject templateObj_;
gc::InitialHeap initialHeap_; gc::InitialHeap initialHeap_;
JSValueType unboxedType_; bool unboxedThis_, unboxedArg_;
MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs, MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType) JSObject* templateObj, gc::InitialHeap initialHeap,
bool unboxedThis, bool unboxedArg)
: MBinaryInstruction(lhs, rhs), : MBinaryInstruction(lhs, rhs),
templateObj_(templateObj), templateObj_(templateObj),
initialHeap_(initialHeap), initialHeap_(initialHeap),
unboxedType_(unboxedType) unboxedThis_(unboxedThis),
unboxedArg_(unboxedArg)
{ {
setResultType(MIRType_Object); setResultType(MIRType_Object);
setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj)); setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
@ -9205,10 +9207,10 @@ class MArrayConcat
static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints, static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
MDefinition* lhs, MDefinition* rhs, MDefinition* lhs, MDefinition* rhs,
JSObject* templateObj, gc::InitialHeap initialHeap, JSObject* templateObj, gc::InitialHeap initialHeap,
JSValueType unboxedType) bool unboxedThis, bool unboxedArg)
{ {
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
initialHeap, unboxedType); initialHeap, unboxedThis, unboxedArg);
} }
JSObject* templateObj() const { JSObject* templateObj() const {
@ -9219,12 +9221,17 @@ class MArrayConcat
return initialHeap_; return initialHeap_;
} }
JSValueType unboxedType() const { bool unboxedThis() const {
return unboxedType_; return unboxedThis_;
}
bool unboxedArg() const {
return unboxedArg_;
} }
AliasSet getAliasSet() const override { AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) | return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedThis() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
AliasSet::BoxedOrUnboxedElements(unboxedArg() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) |
AliasSet::ObjectFields); AliasSet::ObjectFields);
} }
bool possiblyCalls() const override { bool possiblyCalls() const override {

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

@ -2563,40 +2563,41 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
return true; return true;
} }
template <JSValueType Type> template <JSValueType TypeOne, JSValueType TypeTwo>
DenseElementResult DenseElementResult
ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result) ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
{ {
uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<Type>(obj1); uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<TypeOne>(obj1);
MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1)); MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<Type>(obj2); uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<TypeTwo>(obj2);
MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2)); MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
/* No overflow here due to nelements limit. */ /* No overflow here due to nelements limit. */
uint32_t len = initlen1 + initlen2; uint32_t len = initlen1 + initlen2;
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(result) == 0); MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<TypeOne>(result) == 0);
if (!EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, len)) DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<TypeOne>(cx, result, len);
return DenseElementResult::Failure; if (rv != DenseElementResult::Success)
return rv;
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj1, 0, 0, initlen1); CopyBoxedOrUnboxedDenseElements<TypeOne, TypeOne>(cx, result, obj1, 0, 0, initlen1);
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj2, initlen1, 0, initlen2); CopyBoxedOrUnboxedDenseElements<TypeOne, TypeTwo>(cx, result, obj2, initlen1, 0, initlen2);
SetAnyBoxedOrUnboxedArrayLength(cx, result, len); SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
return DenseElementResult::Success; return DenseElementResult::Success;
} }
DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel, DefineBoxedOrUnboxedFunctorPair4(ArrayConcatDenseKernel,
JSContext*, JSObject*, JSObject*, JSObject*); JSContext*, JSObject*, JSObject*, JSObject*);
bool bool
js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2, js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
HandleObject result) HandleObject result)
{ {
ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result); ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result); DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, obj1, obj2);
MOZ_ASSERT(rv != DenseElementResult::Incomplete); MOZ_ASSERT(rv != DenseElementResult::Incomplete);
return rv == DenseElementResult::Success; return rv == DenseElementResult::Success;
} }
@ -2627,17 +2628,63 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen); narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
if (!narr) if (!narr)
return false; return false;
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length); SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
DebugOnly<DenseElementResult> result =
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
MOZ_ASSERT(result.value == DenseElementResult::Success);
args.rval().setObject(*narr); args.rval().setObject(*narr);
if (argc == 0) if (argc == 0)
return true; return true;
argc--; argc--;
p++; p++;
if (length == initlen) {
while (argc) {
HandleValue v = HandleValue::fromMarkedLocation(p);
if (!v.isObject())
break;
RootedObject obj(cx, &v.toObject());
// This should be IsConcatSpreadable
if (!IsArray(obj, cx) || ObjectMayHaveExtraIndexedProperties(obj))
break;
uint32_t argLength;
if (!GetLengthProperty(cx, obj, &argLength))
return false;
initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
if (argLength != initlen)
break;
DenseElementResult result =
EnsureAnyBoxedOrUnboxedDenseElements(cx, narr, length + argLength);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Incomplete)
break;
SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length + argLength);
bool success = true;
for (size_t i = 0; i < initlen; i++) {
Value v = GetAnyBoxedOrUnboxedDenseElement(obj, i);
if (!InitAnyBoxedOrUnboxedDenseElement(cx, narr, length + i, v)) {
success = false;
break;
}
}
if (!success) {
SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length);
break;
}
length += argLength;
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
argc--;
p++;
}
}
} else { } else {
narr = NewDenseEmptyArray(cx); narr = NewDenseEmptyArray(cx);
if (!narr) if (!narr)
@ -2934,9 +2981,10 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
if (initlen > begin) { if (initlen > begin) {
size_t count = Min<size_t>(initlen - begin, end - begin); size_t count = Min<size_t>(initlen - begin, end - begin);
if (count) { if (count) {
if (!EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count)) DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
return DenseElementResult::Failure; if (rv != DenseElementResult::Success)
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj, 0, begin, count); return rv;
CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count);
} }
} }

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

@ -272,6 +272,12 @@ UnboxedArrayObject::triggerPreBarrier(size_t index)
// Combined methods for NativeObject and UnboxedArrayObject accesses. // Combined methods for NativeObject and UnboxedArrayObject accesses.
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
static inline bool
HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
{
return obj->isNative() || obj->is<UnboxedArrayObject>();
}
static inline size_t static inline size_t
GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj) GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
{ {
@ -330,6 +336,16 @@ SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, con
return obj->as<UnboxedArrayObject>().setElement(cx, index, value); return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
} }
static inline bool
InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
{
if (obj->isNative()) {
obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
return true;
}
return obj->as<UnboxedArrayObject>().initElement(cx, index, value);
}
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Template methods for NativeObject and UnboxedArrayObject accesses. // Template methods for NativeObject and UnboxedArrayObject accesses.
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
@ -417,19 +433,19 @@ SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const
} }
template <JSValueType Type> template <JSValueType Type>
static inline bool static inline DenseElementResult
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
{ {
if (Type == JSVAL_TYPE_MAGIC) { if (Type == JSVAL_TYPE_MAGIC) {
if (!obj->as<ArrayObject>().ensureElements(cx, count)) if (!obj->as<ArrayObject>().ensureElements(cx, count))
return false; return DenseElementResult::Failure;
} else { } else {
if (obj->as<UnboxedArrayObject>().capacity() < count) { if (obj->as<UnboxedArrayObject>().capacity() < count) {
if (!obj->as<UnboxedArrayObject>().growElements(cx, count)) if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
return false; return DenseElementResult::Failure;
} }
} }
return true; return DenseElementResult::Success;
} }
template <JSValueType Type> template <JSValueType Type>
@ -547,33 +563,54 @@ MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart,
return DenseElementResult::Success; return DenseElementResult::Success;
} }
template <JSValueType Type> template <JSValueType DstType, JSValueType SrcType>
static inline DenseElementResult static inline DenseElementResult
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
uint32_t dstStart, uint32_t srcStart, uint32_t length) uint32_t dstStart, uint32_t srcStart, uint32_t length)
{ {
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(src)); MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(dst)); MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == dstStart); MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<Type>(dst) >= length); MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(src) >= srcStart + length);
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, dstStart + length); SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
if (Type == JSVAL_TYPE_MAGIC) { if (DstType == JSVAL_TYPE_MAGIC) {
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart; if (SrcType == JSVAL_TYPE_MAGIC) {
dst->as<NativeObject>().initDenseElements(dstStart, vp, length); const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
} else { dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
} else {
for (size_t i = 0; i < length; i++) {
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
dst->as<NativeObject>().initDenseElement(dstStart + i, v);
}
}
} else if (DstType == SrcType) {
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements(); uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
uint8_t* srcData = src->as<UnboxedArrayObject>().elements(); uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
size_t elementSize = UnboxedTypeSize(Type); size_t elementSize = UnboxedTypeSize(DstType);
memcpy(dstData + dstStart * elementSize, memcpy(dstData + dstStart * elementSize,
srcData + srcStart * elementSize, srcData + srcStart * elementSize,
length * elementSize); length * elementSize);
// Add a store buffer entry if we might have copied a nursery pointer to dst. // Add a store buffer entry if we might have copied a nursery pointer to dst.
if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst)) if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCellFromMainThread(dst); dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCellFromMainThread(dst);
} else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
for (size_t i = 0; i < length; i++) {
int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
*reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
}
} else {
for (size_t i = 0; i < length; i++) {
Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
}
} }
return DenseElementResult::Success; return DenseElementResult::Success;
@ -596,19 +633,70 @@ template <typename F>
DenseElementResult DenseElementResult
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj) CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
{ {
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(obj)) if (!HasAnyBoxedOrUnboxedDenseElements(obj))
return DenseElementResult::Incomplete;
switch (GetBoxedOrUnboxedType(obj)) {
case JSVAL_TYPE_MAGIC:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_BOOLEAN>(obj)) case JSVAL_TYPE_BOOLEAN:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_INT32>(obj)) case JSVAL_TYPE_INT32:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_DOUBLE>(obj)) case JSVAL_TYPE_DOUBLE:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_STRING>(obj)) case JSVAL_TYPE_STRING:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_OBJECT>(obj)) case JSVAL_TYPE_OBJECT:
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>(); return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
return DenseElementResult::Incomplete; default:
MOZ_CRASH();
}
}
// As above, except the specialization can reflect the unboxed type of two objects.
template <typename F>
DenseElementResult
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
{
if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
return DenseElementResult::Incomplete;
#define SPECIALIZE_OBJ2(TYPE) \
switch (GetBoxedOrUnboxedType(obj2)) { \
case JSVAL_TYPE_MAGIC: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \
case JSVAL_TYPE_BOOLEAN: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \
case JSVAL_TYPE_INT32: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \
case JSVAL_TYPE_DOUBLE: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \
case JSVAL_TYPE_STRING: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \
case JSVAL_TYPE_OBJECT: \
return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \
default: \
MOZ_CRASH(); \
}
switch (GetBoxedOrUnboxedType(obj1)) {
case JSVAL_TYPE_MAGIC:
SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
case JSVAL_TYPE_BOOLEAN:
SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
case JSVAL_TYPE_INT32:
SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
case JSVAL_TYPE_DOUBLE:
SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
case JSVAL_TYPE_STRING:
SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
case JSVAL_TYPE_OBJECT:
SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
default:
MOZ_CRASH();
}
#undef SPECIALIZE_OBJ2
} }
#undef DEPENDENT_TEMPLATE_HINT #undef DEPENDENT_TEMPLATE_HINT
@ -649,6 +737,18 @@ struct Signature ## Functor { \
} \ } \
} }
#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \
struct Signature ## Functor { \
A a; B b; C c; D d; \
Signature ## Functor(A a, B b, C c, D d) \
: a(a), b(b), c(c), d(d) \
{} \
template <JSValueType TypeOne, JSValueType TypeTwo> \
DenseElementResult operator()() { \
return Signature<TypeOne, TypeTwo>(a, b, c, d); \
} \
}
#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \ #define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \
struct Signature ## Functor { \ struct Signature ## Functor { \
A a; B b; C c; D d; E e; \ A a; B b; C c; D d; E e; \
@ -673,6 +773,18 @@ struct Signature ## Functor { \
} \ } \
} }
#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \
struct Signature ## Functor { \
A a; B b; C c; D d; E e; F f; \
Signature ## Functor(A a, B b, C c, D d, E e, F f) \
: a(a), b(b), c(c), d(d), e(e), f(f) \
{} \
template <JSValueType TypeOne, JSValueType TypeTwo> \
DenseElementResult operator()() { \
return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \
} \
}
DenseElementResult DenseElementResult
SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
uint32_t start, const Value* vp, uint32_t count, uint32_t start, const Value* vp, uint32_t count,
@ -689,7 +801,7 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
void void
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen); SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
bool DenseElementResult
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count); EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
} // namespace js } // namespace js

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

@ -1671,11 +1671,9 @@ CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType*
{ {
if (obj->inDictionaryMode() || if (obj->inDictionaryMode() ||
obj->lastProperty()->propid() != AtomToId(cx->names().length) || obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
!obj->lastProperty()->previous()->isEmptyShape() || !obj->lastProperty()->previous()->isEmptyShape())
!obj->getDenseInitializedLength())
{ {
// Only use an unboxed representation if the object has at // Only use an unboxed representation if the object has no properties.
// least one element, and no properties.
return false; return false;
} }
@ -1830,6 +1828,8 @@ UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
setLength(cx, NextValue(values, valueCursor).toInt32()); setLength(cx, NextValue(values, valueCursor).toInt32());
int32_t initlen = NextValue(values, valueCursor).toInt32(); int32_t initlen = NextValue(values, valueCursor).toInt32();
if (!initlen)
return;
if (!growElements(cx, initlen)) if (!growElements(cx, initlen))
CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert"); CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert");
@ -2055,15 +2055,15 @@ js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
return CallBoxedOrUnboxedSpecialization(functor, obj); return CallBoxedOrUnboxedSpecialization(functor, obj);
} }
DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements, DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t); JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
DenseElementResult DenseElementResult
js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
uint32_t dstStart, uint32_t srcStart, uint32_t length) uint32_t dstStart, uint32_t srcStart, uint32_t length)
{ {
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length); CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
return CallBoxedOrUnboxedSpecialization(functor, dst); return CallBoxedOrUnboxedSpecialization(functor, dst, src);
} }
DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength, DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
@ -2075,3 +2075,13 @@ js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t i
SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen); SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success); JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
} }
DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
JSContext*, JSObject*, size_t);
DenseElementResult
js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
{
EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
return CallBoxedOrUnboxedSpecialization(functor, obj);
}