зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1176451 - Optimize Array.concat when used on different types of boxed vs. unboxed arrays, r=jandem.
This commit is contained in:
Родитель
fe87d76bdf
Коммит
0f1cdda9e1
|
@ -9541,7 +9541,7 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
|||
|
||||
static bool
|
||||
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
|
||||
// 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();
|
||||
|
||||
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
|
||||
// don't end up with a template whose structure might change later.
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject,
|
||||
/* forceAnalyze = */ true));
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
|
||||
if (!res)
|
||||
return false;
|
||||
return true;
|
||||
|
@ -9568,17 +9575,30 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
|
|||
}
|
||||
|
||||
if (native == js::array_concat || native == js::array_slice) {
|
||||
if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) {
|
||||
if (args.thisv().isObject()) {
|
||||
JSObject* obj = &args.thisv().toObject();
|
||||
if (!obj->isSingleton()) {
|
||||
if (obj->group()->maybePreliminaryObjects()) {
|
||||
*skipAttach = true;
|
||||
return true;
|
||||
}
|
||||
res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
|
||||
TenuredObject, /* forceAnalyze = */ true));
|
||||
if (!res)
|
||||
return false;
|
||||
TenuredObject));
|
||||
return !!res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (native == js::str_split && args.length() == 1 && args[0].isString()) {
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject,
|
||||
/* forceAnalyze = */ true));
|
||||
ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
if (group->maybePreliminaryObjects()) {
|
||||
*skipAttach = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
|
||||
if (!res)
|
||||
return false;
|
||||
return true;
|
||||
|
@ -9877,9 +9897,15 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
|||
|
||||
RootedObject templateObject(cx);
|
||||
if (MOZ_LIKELY(!isSpread)) {
|
||||
bool skipAttach = false;
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject))
|
||||
if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject, &skipAttach))
|
||||
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)",
|
||||
|
|
|
@ -7222,22 +7222,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;
|
||||
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);
|
||||
} else {
|
||||
if (lir->mir()->unboxedThis()) {
|
||||
masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
|
||||
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
|
||||
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.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
|
||||
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.
|
||||
|
|
|
@ -864,9 +864,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
|||
if (!thisTypes || !argTypes)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
const Class* clasp = thisTypes->getKnownClass(constraints());
|
||||
if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
|
||||
const Class* thisClasp = thisTypes->getKnownClass(constraints());
|
||||
if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_);
|
||||
if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
|
@ -874,8 +875,10 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
|||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
if (argTypes->getKnownClass(constraints()) != clasp)
|
||||
const Class* argClasp = argTypes->getKnownClass(constraints());
|
||||
if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_)
|
||||
return InliningStatus_NotInlined;
|
||||
bool unboxedArg = (argClasp == &UnboxedArrayObject::class_);
|
||||
if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW))
|
||||
{
|
||||
|
@ -883,15 +886,6 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
|||
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.
|
||||
if (ArrayPrototypeHasIndexedProperty(this, script())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
|
||||
|
@ -951,7 +945,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo)
|
|||
MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg,
|
||||
templateObj,
|
||||
templateObj->group()->initialHeap(constraints()),
|
||||
unboxedType);
|
||||
unboxedThis, unboxedArg);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
|
|
@ -9186,14 +9186,16 @@ class MArrayConcat
|
|||
{
|
||||
CompilerObject templateObj_;
|
||||
gc::InitialHeap initialHeap_;
|
||||
JSValueType unboxedType_;
|
||||
bool unboxedThis_, unboxedArg_;
|
||||
|
||||
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),
|
||||
templateObj_(templateObj),
|
||||
initialHeap_(initialHeap),
|
||||
unboxedType_(unboxedType)
|
||||
unboxedThis_(unboxedThis),
|
||||
unboxedArg_(unboxedArg)
|
||||
{
|
||||
setResultType(MIRType_Object);
|
||||
setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
|
||||
|
@ -9205,10 +9207,10 @@ class MArrayConcat
|
|||
static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
MDefinition* lhs, MDefinition* rhs,
|
||||
JSObject* templateObj, gc::InitialHeap initialHeap,
|
||||
JSValueType unboxedType)
|
||||
bool unboxedThis, bool unboxedArg)
|
||||
{
|
||||
return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
|
||||
initialHeap, unboxedType);
|
||||
initialHeap, unboxedThis, unboxedArg);
|
||||
}
|
||||
|
||||
JSObject* templateObj() const {
|
||||
|
@ -9219,12 +9221,17 @@ class MArrayConcat
|
|||
return initialHeap_;
|
||||
}
|
||||
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
bool unboxedThis() const {
|
||||
return unboxedThis_;
|
||||
}
|
||||
|
||||
bool unboxedArg() const {
|
||||
return unboxedArg_;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
|
|
|
@ -2563,32 +2563,33 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
|
|||
return true;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
template <JSValueType TypeOne, JSValueType TypeTwo>
|
||||
DenseElementResult
|
||||
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));
|
||||
|
||||
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<Type>(obj2);
|
||||
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<TypeTwo>(obj2);
|
||||
MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
|
||||
|
||||
/* No overflow here due to nelements limit. */
|
||||
uint32_t len = initlen1 + initlen2;
|
||||
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(result) == 0);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<TypeOne>(result) == 0);
|
||||
|
||||
if (!EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, len))
|
||||
return DenseElementResult::Failure;
|
||||
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<TypeOne>(cx, result, len);
|
||||
if (rv != DenseElementResult::Success)
|
||||
return rv;
|
||||
|
||||
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj1, 0, 0, initlen1);
|
||||
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj2, initlen1, 0, initlen2);
|
||||
CopyBoxedOrUnboxedDenseElements<TypeOne, TypeOne>(cx, result, obj1, 0, 0, initlen1);
|
||||
CopyBoxedOrUnboxedDenseElements<TypeOne, TypeTwo>(cx, result, obj2, initlen1, 0, initlen2);
|
||||
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel,
|
||||
DefineBoxedOrUnboxedFunctorPair4(ArrayConcatDenseKernel,
|
||||
JSContext*, JSObject*, JSObject*, JSObject*);
|
||||
|
||||
bool
|
||||
|
@ -2596,7 +2597,7 @@ js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
|
|||
HandleObject result)
|
||||
{
|
||||
ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
|
||||
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
|
||||
DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, obj1, obj2);
|
||||
MOZ_ASSERT(rv != DenseElementResult::Incomplete);
|
||||
return rv == DenseElementResult::Success;
|
||||
}
|
||||
|
@ -2627,17 +2628,63 @@ js::array_concat(JSContext* cx, unsigned argc, Value* vp)
|
|||
narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
|
||||
if (!narr)
|
||||
return false;
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
|
||||
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
|
||||
|
||||
args.rval().setObject(*narr);
|
||||
if (argc == 0)
|
||||
return true;
|
||||
argc--;
|
||||
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 {
|
||||
narr = NewDenseEmptyArray(cx);
|
||||
if (!narr)
|
||||
|
@ -2934,9 +2981,10 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
|
|||
if (initlen > begin) {
|
||||
size_t count = Min<size_t>(initlen - begin, end - begin);
|
||||
if (count) {
|
||||
if (!EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count))
|
||||
return DenseElementResult::Failure;
|
||||
CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj, 0, begin, count);
|
||||
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
|
||||
if (rv != DenseElementResult::Success)
|
||||
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.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline bool
|
||||
HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
{
|
||||
return obj->isNative() || obj->is<UnboxedArrayObject>();
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
|
||||
{
|
||||
|
@ -330,6 +336,16 @@ SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, con
|
|||
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.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
@ -417,19 +433,19 @@ SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const
|
|||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
static inline DenseElementResult
|
||||
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
if (!obj->as<ArrayObject>().ensureElements(cx, count))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
} else {
|
||||
if (obj->as<UnboxedArrayObject>().capacity() < count) {
|
||||
if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
|
||||
return false;
|
||||
return DenseElementResult::Failure;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
|
@ -547,33 +563,54 @@ MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart,
|
|||
return DenseElementResult::Success;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
template <JSValueType DstType, JSValueType SrcType>
|
||||
static inline DenseElementResult
|
||||
CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == dstStart);
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedCapacity<Type>(dst) >= length);
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
|
||||
MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
|
||||
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
|
||||
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) {
|
||||
if (SrcType == JSVAL_TYPE_MAGIC) {
|
||||
const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
|
||||
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* srcData = src->as<UnboxedArrayObject>().elements();
|
||||
size_t elementSize = UnboxedTypeSize(Type);
|
||||
size_t elementSize = UnboxedTypeSize(DstType);
|
||||
|
||||
memcpy(dstData + dstStart * elementSize,
|
||||
srcData + srcStart * elementSize,
|
||||
length * elementSize);
|
||||
|
||||
// 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);
|
||||
} 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;
|
||||
|
@ -596,19 +633,70 @@ template <typename F>
|
|||
DenseElementResult
|
||||
CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
|
||||
{
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_BOOLEAN>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_INT32>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_DOUBLE>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_STRING>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
|
||||
if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_OBJECT>(obj))
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
|
||||
if (!HasAnyBoxedOrUnboxedDenseElements(obj))
|
||||
return DenseElementResult::Incomplete;
|
||||
switch (GetBoxedOrUnboxedType(obj)) {
|
||||
case JSVAL_TYPE_MAGIC:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
|
||||
case JSVAL_TYPE_BOOLEAN:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
|
||||
case JSVAL_TYPE_INT32:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
|
||||
case JSVAL_TYPE_DOUBLE:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
|
||||
case JSVAL_TYPE_STRING:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
|
||||
case JSVAL_TYPE_OBJECT:
|
||||
return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
|
||||
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
|
||||
|
@ -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) \
|
||||
struct Signature ## Functor { \
|
||||
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
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
|
||||
uint32_t start, const Value* vp, uint32_t count,
|
||||
|
@ -689,7 +801,7 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
|||
void
|
||||
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
|
||||
|
||||
bool
|
||||
DenseElementResult
|
||||
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -1671,11 +1671,9 @@ CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType*
|
|||
{
|
||||
if (obj->inDictionaryMode() ||
|
||||
obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
|
||||
!obj->lastProperty()->previous()->isEmptyShape() ||
|
||||
!obj->getDenseInitializedLength())
|
||||
!obj->lastProperty()->previous()->isEmptyShape())
|
||||
{
|
||||
// Only use an unboxed representation if the object has at
|
||||
// least one element, and no properties.
|
||||
// Only use an unboxed representation if the object has no properties.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1830,6 +1828,8 @@ UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
|
|||
setLength(cx, NextValue(values, valueCursor).toInt32());
|
||||
|
||||
int32_t initlen = NextValue(values, valueCursor).toInt32();
|
||||
if (!initlen)
|
||||
return;
|
||||
|
||||
if (!growElements(cx, initlen))
|
||||
CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert");
|
||||
|
@ -2055,7 +2055,7 @@ js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
|
|||
return CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements,
|
||||
DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
|
||||
JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
DenseElementResult
|
||||
|
@ -2063,7 +2063,7 @@ js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* s
|
|||
uint32_t dstStart, uint32_t srcStart, uint32_t length)
|
||||
{
|
||||
CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, dst);
|
||||
return CallBoxedOrUnboxedSpecialization(functor, dst, src);
|
||||
}
|
||||
|
||||
DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
|
||||
|
@ -2075,3 +2075,13 @@ js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t i
|
|||
SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
|
||||
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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче