Optimize Array.reverse for HOLEY arrays
Array.reverse was optimized only for PACKED_* arrays. This CL adds fast paths for HOLEY_* arrays as well. Bug: chromium:1305342 Change-Id: I83c5ffa6e823478992c2caabd9a88d405b35e464 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4062673 Commit-Queue: Choongwoo Han <choongwoo.han@microsoft.com> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Cr-Commit-Position: refs/heads/main@{#84580}
This commit is contained in:
parent
c776436e7b
commit
af84cc0b7b
@ -3,65 +3,50 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
namespace array {
|
||||
macro LoadElement<ElementsAccessor : type extends ElementsKind, T: type>(
|
||||
macro LoadElement<Elements : type extends FixedArrayBase, T: type>(
|
||||
elements: FixedArrayBase, index: Smi): T;
|
||||
|
||||
LoadElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi): Smi {
|
||||
LoadElement<FixedArray, Object>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi): Object {
|
||||
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
|
||||
return UnsafeCast<Smi>(elements.objects[index]);
|
||||
return elements.objects[index];
|
||||
}
|
||||
|
||||
LoadElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi): JSAny {
|
||||
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
|
||||
return UnsafeCast<JSAny>(elements.objects[index]);
|
||||
}
|
||||
|
||||
LoadElement<array::FastPackedDoubleElements, float64>(
|
||||
implicit context: Context)(elements: FixedArrayBase, index: Smi): float64 {
|
||||
LoadElement<FixedDoubleArray, float64_or_hole>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi): float64_or_hole {
|
||||
const elements: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
|
||||
// This macro is only used for PACKED_DOUBLE, loading the hole should
|
||||
// be impossible.
|
||||
return elements.floats[index].Value() otherwise unreachable;
|
||||
return elements.floats[index];
|
||||
}
|
||||
|
||||
macro StoreElement<ElementsAccessor : type extends ElementsKind, T: type>(
|
||||
macro StoreElement<Elements : type extends FixedArrayBase, T: type>(
|
||||
implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: T): void;
|
||||
|
||||
StoreElement<array::FastPackedSmiElements, Smi>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: Smi): void {
|
||||
const elems: FixedArray = UnsafeCast<FixedArray>(elements);
|
||||
StoreFixedArrayElement(elems, index, value);
|
||||
}
|
||||
|
||||
StoreElement<array::FastPackedObjectElements, JSAny>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: JSAny): void {
|
||||
StoreElement<FixedArray, Object>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: Object): void {
|
||||
const elements: FixedArray = UnsafeCast<FixedArray>(elements);
|
||||
elements.objects[index] = value;
|
||||
}
|
||||
|
||||
StoreElement<array::FastPackedDoubleElements, float64>(
|
||||
implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: float64): void {
|
||||
StoreElement<FixedDoubleArray, float64_or_hole>(implicit context: Context)(
|
||||
elements: FixedArrayBase, index: Smi, value: float64_or_hole): void {
|
||||
const elems: FixedDoubleArray = UnsafeCast<FixedDoubleArray>(elements);
|
||||
StoreFixedDoubleArrayElement(elems, index, value);
|
||||
elems.floats[index] = value;
|
||||
}
|
||||
|
||||
// Fast-path for all PACKED_* elements kinds. These do not need to check
|
||||
// whether a property is present, so we can simply swap them using fast
|
||||
// FixedArray loads/stores.
|
||||
macro FastPackedArrayReverse<Accessor: type, T: type>(
|
||||
macro FastArrayReverse<Elements : type extends FixedArrayBase, T: type>(
|
||||
implicit context: Context)(elements: FixedArrayBase, length: Smi): void {
|
||||
let lower: Smi = 0;
|
||||
let upper: Smi = length - 1;
|
||||
|
||||
while (lower < upper) {
|
||||
const lowerValue: T = LoadElement<Accessor, T>(elements, lower);
|
||||
const upperValue: T = LoadElement<Accessor, T>(elements, upper);
|
||||
StoreElement<Accessor>(elements, lower, upperValue);
|
||||
StoreElement<Accessor>(elements, upper, lowerValue);
|
||||
const lowerValue: T = LoadElement<Elements, T>(elements, lower);
|
||||
const upperValue: T = LoadElement<Elements, T>(elements, upper);
|
||||
StoreElement<Elements>(elements, lower, upperValue);
|
||||
StoreElement<Elements>(elements, upper, lowerValue);
|
||||
++lower;
|
||||
--upper;
|
||||
}
|
||||
@ -144,20 +129,28 @@ macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny):
|
||||
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
|
||||
const kind: ElementsKind = array.map.elements_kind;
|
||||
if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
|
||||
if (kind == ElementsKind::PACKED_SMI_ELEMENTS ||
|
||||
kind == ElementsKind::PACKED_ELEMENTS) {
|
||||
array::EnsureWriteableFastElements(array);
|
||||
FastPackedArrayReverse<array::FastPackedSmiElements, Smi>(
|
||||
array.elements, array.length);
|
||||
} else if (kind == ElementsKind::PACKED_ELEMENTS) {
|
||||
array::EnsureWriteableFastElements(array);
|
||||
FastPackedArrayReverse<array::FastPackedObjectElements, JSAny>(
|
||||
array.elements, array.length);
|
||||
FastArrayReverse<FixedArray, Object>(array.elements, array.length);
|
||||
} else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
|
||||
FastPackedArrayReverse<array::FastPackedDoubleElements, float64>(
|
||||
FastArrayReverse<FixedDoubleArray, float64_or_hole>(
|
||||
array.elements, array.length);
|
||||
} else {
|
||||
if (!IsPrototypeInitialArrayPrototype(array.map)) goto Slow;
|
||||
if (IsNoElementsProtectorCellInvalid()) goto Slow;
|
||||
|
||||
if (kind == ElementsKind::HOLEY_SMI_ELEMENTS ||
|
||||
kind == ElementsKind::HOLEY_ELEMENTS) {
|
||||
array::EnsureWriteableFastElements(array);
|
||||
FastArrayReverse<FixedArray, Object>(array.elements, array.length);
|
||||
} else if (kind == ElementsKind::HOLEY_DOUBLE_ELEMENTS) {
|
||||
FastArrayReverse<FixedDoubleArray, float64_or_hole>(
|
||||
array.elements, array.length);
|
||||
} else {
|
||||
goto Slow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.reverse
|
||||
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
namespace array {
|
||||
macro FastPackedArrayToReversed<Accessor: type, T: type>(
|
||||
macro FastArrayToReversed<Elements : type extends FixedArrayBase, T: type>(
|
||||
implicit context: Context)(
|
||||
kind: constexpr ElementsKind, elements: FixedArrayBase,
|
||||
length: Smi): JSArray {
|
||||
@ -21,10 +21,10 @@ macro FastPackedArrayToReversed<Accessor: type, T: type>(
|
||||
const from = length - k - 1;
|
||||
|
||||
// c. Let fromValue be ? Get(O, from).
|
||||
const fromValue: T = LoadElement<Accessor, T>(elements, from);
|
||||
const fromValue: T = LoadElement<Elements, T>(elements, from);
|
||||
|
||||
// d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
|
||||
StoreElement<Accessor>(copy, k, fromValue);
|
||||
StoreElement<Elements>(copy, k, fromValue);
|
||||
|
||||
// e. Set k to k + 1.
|
||||
++k;
|
||||
@ -35,7 +35,7 @@ macro FastPackedArrayToReversed<Accessor: type, T: type>(
|
||||
return NewJSArray(map, copy);
|
||||
}
|
||||
|
||||
macro TryFastPackedArrayToReversed(implicit context: Context)(receiver: JSAny):
|
||||
macro TryFastArrayToReversed(implicit context: Context)(receiver: JSAny):
|
||||
JSArray labels Slow {
|
||||
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
|
||||
@ -43,19 +43,31 @@ macro TryFastPackedArrayToReversed(implicit context: Context)(receiver: JSAny):
|
||||
|
||||
const kind: ElementsKind = array.map.elements_kind;
|
||||
if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
|
||||
return FastPackedArrayToReversed<array::FastPackedSmiElements, Smi>(
|
||||
return FastArrayToReversed<FixedArray, Object>(
|
||||
ElementsKind::PACKED_SMI_ELEMENTS, array.elements, array.length);
|
||||
}
|
||||
if (kind == ElementsKind::PACKED_ELEMENTS) {
|
||||
return FastPackedArrayToReversed<array::FastPackedObjectElements, JSAny>(
|
||||
} else if (kind == ElementsKind::PACKED_ELEMENTS) {
|
||||
return FastArrayToReversed<FixedArray, Object>(
|
||||
ElementsKind::PACKED_ELEMENTS, array.elements, array.length);
|
||||
}
|
||||
if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
|
||||
return FastPackedArrayToReversed<array::FastPackedDoubleElements, float64>(
|
||||
} else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
|
||||
return FastArrayToReversed<FixedDoubleArray, float64_or_hole>(
|
||||
ElementsKind::PACKED_DOUBLE_ELEMENTS, array.elements, array.length);
|
||||
} else {
|
||||
if (!IsPrototypeInitialArrayPrototype(array.map)) goto Slow;
|
||||
if (IsNoElementsProtectorCellInvalid()) goto Slow;
|
||||
|
||||
if (kind == ElementsKind::HOLEY_SMI_ELEMENTS) {
|
||||
return FastArrayToReversed<FixedArray, Object>(
|
||||
ElementsKind::HOLEY_SMI_ELEMENTS, array.elements, array.length);
|
||||
} else if (kind == ElementsKind::HOLEY_ELEMENTS) {
|
||||
return FastArrayToReversed<FixedArray, Object>(
|
||||
ElementsKind::HOLEY_ELEMENTS, array.elements, array.length);
|
||||
} else if (kind == ElementsKind::HOLEY_DOUBLE_ELEMENTS) {
|
||||
return FastArrayToReversed<FixedDoubleArray, float64_or_hole>(
|
||||
ElementsKind::HOLEY_DOUBLE_ELEMENTS, array.elements, array.length);
|
||||
}
|
||||
|
||||
goto Slow;
|
||||
}
|
||||
}
|
||||
|
||||
transitioning builtin GenericArrayToReversed(implicit context: Context)(
|
||||
@ -96,7 +108,7 @@ transitioning builtin GenericArrayToReversed(implicit context: Context)(
|
||||
transitioning javascript builtin ArrayPrototypeToReversed(
|
||||
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
||||
try {
|
||||
return TryFastPackedArrayToReversed(receiver) otherwise Slow;
|
||||
return TryFastArrayToReversed(receiver) otherwise Slow;
|
||||
} label Slow {
|
||||
return GenericArrayToReversed(receiver);
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ assertArrayEquals(["str4", "str3", "str2"], ["str2", "str3", "str4"].reverse());
|
||||
assertArrayEquals([4,3,,1], [1,,3,4].reverse());
|
||||
assertArrayEquals([4,,2,1], [1,2,,4].reverse());
|
||||
assertArrayEquals([5,,3,,1], [1,,3,,5].reverse());
|
||||
assertArrayEquals([0.5,,0.3,,0.1], [0.1,,0.3,,0.5].reverse());
|
||||
assertArrayEquals(["5",,"3",,"1"], ["1",,"3",,"5"].reverse());
|
||||
|
||||
function TestReverseWithObject() {
|
||||
let obj = { length: 5 };
|
||||
|
@ -31,6 +31,30 @@ assertEquals("toReversed", Array.prototype.toReversed.name);
|
||||
assertFalse(a === r);
|
||||
})();
|
||||
|
||||
(function TestSmiHoley() {
|
||||
let a = [1,,3,4];
|
||||
let r = a.toReversed();
|
||||
assertEquals([1,,3,4], a);
|
||||
assertEquals([4,3,,1], r);
|
||||
assertFalse(a === r);
|
||||
})();
|
||||
|
||||
(function TestDoubleHoley() {
|
||||
let a = [1.1,,3.3,4.4];
|
||||
let r = a.toReversed();
|
||||
assertEquals([1.1,,3.3,4.4], a);
|
||||
assertEquals([4.4,3.3,,1.1], r);
|
||||
assertFalse(a === r);
|
||||
})();
|
||||
|
||||
(function TestHoley() {
|
||||
let a = [true,false,,1,42.42];
|
||||
let r = a.toReversed();
|
||||
assertEquals([true,false,,1,42.42], a);
|
||||
assertEquals([42.42,1,,false,true], r);
|
||||
assertFalse(a === r);
|
||||
})();
|
||||
|
||||
(function TestGeneric() {
|
||||
let a = { length: 4,
|
||||
get "0"() { return "hello"; },
|
||||
|
Loading…
Reference in New Issue
Block a user