Bug 1884360 part 6 - Use std_Array_sort in Array.prototype.toSorted. r=iain

Longer-term we could make `toSorted` itself a trampoline native, but that's more
complicated and this patch gets us most of the performance and code reuse benefits.

Differential Revision: https://phabricator.services.mozilla.com/D204629
This commit is contained in:
Jan de Mooij 2024-03-27 11:18:35 +00:00
parent 9e031a7731
commit fa62f291f8
5 changed files with 3 additions and 179 deletions

View File

@ -2028,46 +2028,6 @@ static bool FillWithUndefined(JSContext* cx, HandleObject obj, uint32_t start,
return true;
}
static bool ArrayNativeSortImpl(JSContext* cx, Handle<JSObject*> obj,
Handle<Value> fval, ComparatorMatchResult comp);
bool js::intrinsic_ArrayNativeSort(JSContext* cx, unsigned argc, Value* vp) {
// This function is called from the self-hosted Array.prototype.sort
// implementation. It returns |true| if the array was sorted, otherwise it
// returns |false| to notify the self-hosted code to perform the sorting.
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
HandleValue fval = args[0];
MOZ_ASSERT(fval.isUndefined() || IsCallable(fval));
ComparatorMatchResult comp;
if (fval.isObject()) {
comp = MatchNumericComparator(cx, &fval.toObject());
if (comp == Match_Failure) {
return false;
}
if (comp == Match_None) {
// Non-optimized user supplied comparators perform much better when
// called from within a self-hosted sorting function.
args.rval().setBoolean(false);
return true;
}
} else {
comp = Match_None;
}
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
if (!ArrayNativeSortImpl(cx, obj, fval, comp)) {
return false;
}
args.rval().setBoolean(true);
return true;
}
static bool ArrayNativeSortImpl(JSContext* cx, Handle<JSObject*> obj,
Handle<Value> fval,
ComparatorMatchResult comp) {

View File

@ -111,9 +111,6 @@ extern bool GetElements(JSContext* cx, HandleObject aobj, uint32_t length,
/* Natives exposed for optimization by the interpreter and JITs. */
extern bool intrinsic_ArrayNativeSort(JSContext* cx, unsigned argc,
js::Value* vp);
extern bool array_includes(JSContext* cx, unsigned argc, js::Value* vp);
extern bool array_indexOf(JSContext* cx, unsigned argc, js::Value* vp);
extern bool array_lastIndexOf(JSContext* cx, unsigned argc, js::Value* vp);

View File

@ -76,29 +76,6 @@ function ArraySome(callbackfn /*, thisArg*/) {
// Inlining this enables inlining of the callback function.
SetIsInlinableLargeFunction(ArraySome);
// ES2023 draft rev cb4224156c54156f30c18c50784c1b0148ebfae5
// 23.1.3.30 Array.prototype.sort ( comparefn )
function ArraySortCompare(comparefn) {
return function(x, y) {
// Steps 4.a-c.
if (x === undefined) {
if (y === undefined) {
return 0;
}
return 1;
}
if (y === undefined) {
return -1;
}
// Step 4.d.i.
var v = ToNumber(callContentFunction(comparefn, undefined, x, y));
// Steps 4.d.ii-iii.
return v !== v ? 0 : v;
};
}
/* ES5 15.4.4.18. */
function ArrayForEach(callbackfn /*, thisArg*/) {
/* Step 1. */
@ -1279,22 +1256,8 @@ function ArrayToSorted(comparefn) {
return items;
}
// First try to sort the array in native code, if that fails, indicated by
// returning |false| from ArrayNativeSort, sort it in self-hosted code.
if (callFunction(ArrayNativeSort, items, comparefn)) {
return items;
}
// Step 5.
var wrappedCompareFn = ArraySortCompare(comparefn);
// Steps 6-9.
var sorted = MergeSort(items, len, wrappedCompareFn);
assert(IsPackedArray(sorted), "sorted is a packed array");
assert(sorted.length === len, "sorted array has the correct length");
return sorted;
// Steps 5-9.
return callFunction(std_Array_sort, items, comparefn);
}
// https://github.com/tc39/proposal-array-find-from-last

View File

@ -6,7 +6,7 @@
// consolidated here to avoid confusion and re-implementation of existing
// algorithms.
// For sorting small arrays.
// For sorting small typed arrays.
function InsertionSort(array, from, to, comparefn) {
var item, swap, i, j;
for (i = from + 1; i <= to; i++) {
@ -22,101 +22,6 @@ function InsertionSort(array, from, to, comparefn) {
}
}
// A helper function for MergeSort.
//
// Merge comparefn-sorted slices list[start..<=mid] and list[mid+1..<=end],
// storing the merged sequence in out[start..<=end].
function Merge(list, out, start, mid, end, comparefn) {
// Skip lopsided runs to avoid doing useless work.
// Skip calling the comparator if the sub-list is already sorted.
if (
mid >= end ||
callContentFunction(comparefn, undefined, list[mid], list[mid + 1]) <= 0
) {
for (var i = start; i <= end; i++) {
DefineDataProperty(out, i, list[i]);
}
return;
}
var i = start;
var j = mid + 1;
var k = start;
while (i <= mid && j <= end) {
var lvalue = list[i];
var rvalue = list[j];
if (callContentFunction(comparefn, undefined, lvalue, rvalue) <= 0) {
DefineDataProperty(out, k++, lvalue);
i++;
} else {
DefineDataProperty(out, k++, rvalue);
j++;
}
}
// Empty out any remaining elements.
while (i <= mid) {
DefineDataProperty(out, k++, list[i++]);
}
while (j <= end) {
DefineDataProperty(out, k++, list[j++]);
}
}
// Helper function for overwriting a sparse array with a
// dense array, filling remaining slots with holes.
function MoveHoles(sparse, sparseLen, dense, denseLen) {
for (var i = 0; i < denseLen; i++) {
sparse[i] = dense[i];
}
for (var j = denseLen; j < sparseLen; j++) {
delete sparse[j];
}
}
// Iterative, bottom up, mergesort.
function MergeSort(array, len, comparefn) {
assert(IsPackedArray(array), "array is packed");
assert(array.length === len, "length mismatch");
assert(len > 0, "array should be non-empty");
// Insertion sort for small arrays, where "small" is defined by performance
// testing.
if (len < 24) {
InsertionSort(array, 0, len - 1, comparefn);
return array;
}
// We do all of our allocating up front
var lBuffer = array;
var rBuffer = [];
// Use insertion sort for initial ranges.
var windowSize = 4;
for (var start = 0; start < len - 1; start += windowSize) {
var end = std_Math_min(start + windowSize - 1, len - 1);
InsertionSort(lBuffer, start, end, comparefn);
}
for (; windowSize < len; windowSize = 2 * windowSize) {
for (var start = 0; start < len; start += 2 * windowSize) {
// The midpoint between the two subarrays.
var mid = start + windowSize - 1;
// To keep from going over the edge.
var end = std_Math_min(start + 2 * windowSize - 1, len - 1);
Merge(lBuffer, rBuffer, start, mid, end, comparefn);
}
// Swap both lists.
var swap = lBuffer;
lBuffer = rBuffer;
rBuffer = swap;
}
return lBuffer;
}
// A helper function for MergeSortTypedArray.
//
// Merge comparefn-sorted slices list[start..<=mid] and list[mid+1..<=end],

View File

@ -1963,7 +1963,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("ArrayIteratorPrototypeOptimizable",
intrinsic_ArrayIteratorPrototypeOptimizable, 0, 0,
IntrinsicArrayIteratorPrototypeOptimizable),
JS_FN("ArrayNativeSort", intrinsic_ArrayNativeSort, 1, 0),
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1, 0),
JS_FN("CallArrayBufferMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ArrayBufferObject>>, 2, 0),