[array] Change fast- to slow-path transition for Array#sort
With the recent changes to Array#sort, the main algorithm does not need to bail out anymore. Only the initial copying into the workarray, as well as the final copying back into the original backing store might cause a switch from fast-path to the slow-path. This CL changes the slow-path so sorting itself is not restarted and the slow-path will continue copying where the fast-path left off. R=jgruber@chromium.org Bug: v8:7382 Change-Id: I4ab61daa62bb816f4f6e16e60bde1f948ad1e7db Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1507717 Commit-Queue: Simon Zünd <szuend@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#60151}
This commit is contained in:
parent
2dac9b80b3
commit
e416f4da69
75
third_party/v8/builtins/array-sort.tq
vendored
75
third_party/v8/builtins/array-sort.tq
vendored
@ -14,15 +14,6 @@
|
|||||||
// https://github.com/python/cpython/blob/master/Objects/listsort.txt
|
// https://github.com/python/cpython/blob/master/Objects/listsort.txt
|
||||||
|
|
||||||
namespace array {
|
namespace array {
|
||||||
// All accessors bail to the GenericElementsAccessor if assumptions checked
|
|
||||||
// by "CanUseSameAccessor<>" are violated:
|
|
||||||
// Generic <- FastPackedSmi
|
|
||||||
// <- FastSmiOrObject
|
|
||||||
// <- FastDouble
|
|
||||||
// <- Dictionary
|
|
||||||
const kGenericElementsAccessorId: Smi = 0;
|
|
||||||
const kFastElementsAccessorId: Smi = 1;
|
|
||||||
|
|
||||||
class SortState {
|
class SortState {
|
||||||
Compare(implicit context: Context)(x: Object, y: Object): Number {
|
Compare(implicit context: Context)(x: Object, y: Object): Number {
|
||||||
const sortCompare: CompareBuiltinFn = this.sortComparePtr;
|
const sortCompare: CompareBuiltinFn = this.sortComparePtr;
|
||||||
@ -40,6 +31,12 @@ namespace array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResetToGenericAccessor() {
|
||||||
|
this.loadFn = Load<GenericElementsAccessor>;
|
||||||
|
this.storeFn = Store<GenericElementsAccessor>;
|
||||||
|
this.bailoutStatus = kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
// The receiver of the Array.p.sort call.
|
// The receiver of the Array.p.sort call.
|
||||||
receiver: JSReceiver;
|
receiver: JSReceiver;
|
||||||
|
|
||||||
@ -93,10 +90,6 @@ namespace array {
|
|||||||
|
|
||||||
// Pointer to the temporary array.
|
// Pointer to the temporary array.
|
||||||
tempArray: FixedArray;
|
tempArray: FixedArray;
|
||||||
|
|
||||||
// A Smi constant describing which accessors to use. This is used
|
|
||||||
// for reloading the right elements and for a sanity check.
|
|
||||||
accessor: Smi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro NewSortState(implicit context: Context)(
|
macro NewSortState(implicit context: Context)(
|
||||||
@ -106,7 +99,6 @@ namespace array {
|
|||||||
const sortComparePtr =
|
const sortComparePtr =
|
||||||
comparefn != Undefined ? SortCompareUserFn : SortCompareDefault;
|
comparefn != Undefined ? SortCompareUserFn : SortCompareDefault;
|
||||||
const map = receiver.map;
|
const map = receiver.map;
|
||||||
let accessor = kGenericElementsAccessorId;
|
|
||||||
let loadFn = Load<GenericElementsAccessor>;
|
let loadFn = Load<GenericElementsAccessor>;
|
||||||
let storeFn = Store<GenericElementsAccessor>;
|
let storeFn = Store<GenericElementsAccessor>;
|
||||||
let canUseSameAccessorFn = CanUseSameAccessor<GenericElementsAccessor>;
|
let canUseSameAccessorFn = CanUseSameAccessor<GenericElementsAccessor>;
|
||||||
@ -117,7 +109,6 @@ namespace array {
|
|||||||
let a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
let a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||||
|
|
||||||
const elementsKind: ElementsKind = map.elements_kind;
|
const elementsKind: ElementsKind = map.elements_kind;
|
||||||
accessor = kFastElementsAccessorId;
|
|
||||||
if (IsDoubleElementsKind(elementsKind)) {
|
if (IsDoubleElementsKind(elementsKind)) {
|
||||||
loadFn = Load<FastDoubleElements>;
|
loadFn = Load<FastDoubleElements>;
|
||||||
storeFn = Store<FastDoubleElements>;
|
storeFn = Store<FastDoubleElements>;
|
||||||
@ -136,7 +127,6 @@ namespace array {
|
|||||||
label Slow {
|
label Slow {
|
||||||
if (map.elements_kind == DICTIONARY_ELEMENTS && IsExtensibleMap(map) &&
|
if (map.elements_kind == DICTIONARY_ELEMENTS && IsExtensibleMap(map) &&
|
||||||
!IsCustomElementsReceiverInstanceType(map.instance_type)) {
|
!IsCustomElementsReceiverInstanceType(map.instance_type)) {
|
||||||
accessor = kFastElementsAccessorId;
|
|
||||||
loadFn = Load<DictionaryElements>;
|
loadFn = Load<DictionaryElements>;
|
||||||
storeFn = Store<DictionaryElements>;
|
storeFn = Store<DictionaryElements>;
|
||||||
canUseSameAccessorFn = CanUseSameAccessor<DictionaryElements>;
|
canUseSameAccessorFn = CanUseSameAccessor<DictionaryElements>;
|
||||||
@ -157,8 +147,7 @@ namespace array {
|
|||||||
0,
|
0,
|
||||||
AllocateZeroedFixedArray(Convert<intptr>(kMaxMergePending)),
|
AllocateZeroedFixedArray(Convert<intptr>(kMaxMergePending)),
|
||||||
AllocateZeroedFixedArray(Convert<intptr>(sortLength)),
|
AllocateZeroedFixedArray(Convert<intptr>(sortLength)),
|
||||||
kEmptyFixedArray,
|
kEmptyFixedArray
|
||||||
accessor
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1296,55 +1285,59 @@ namespace array {
|
|||||||
|
|
||||||
transitioning macro
|
transitioning macro
|
||||||
CopyReceiverElementsToWorkArray(
|
CopyReceiverElementsToWorkArray(
|
||||||
implicit context: Context, sortState: SortState)(length: Smi)
|
implicit context: Context, sortState: SortState)(length: Smi) {
|
||||||
labels Bailout {
|
|
||||||
// TODO(szuend): Investigate if we can use COW arrays or a memcpy + range
|
// TODO(szuend): Investigate if we can use COW arrays or a memcpy + range
|
||||||
// barrier to speed this step up.
|
// barrier to speed this step up.
|
||||||
const loadFn = sortState.loadFn;
|
let loadFn = sortState.loadFn;
|
||||||
const workArray = sortState.workArray;
|
const workArray = sortState.workArray;
|
||||||
|
|
||||||
for (let i: Smi = 0; i < length; ++i) {
|
for (let i: Smi = 0; i < length; ++i) {
|
||||||
workArray.objects[i] = CallLoad(loadFn, i) otherwise Bailout;
|
try {
|
||||||
|
workArray.objects[i] = CallLoad(loadFn, i) otherwise Bailout;
|
||||||
|
}
|
||||||
|
label Bailout deferred {
|
||||||
|
sortState.ResetToGenericAccessor();
|
||||||
|
loadFn = sortState.loadFn;
|
||||||
|
workArray.objects[i] = CallLoad(loadFn, i) otherwise unreachable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transitioning macro
|
transitioning macro
|
||||||
CopyWorkArrayToReceiver(implicit context: Context, sortState: SortState)(
|
CopyWorkArrayToReceiver(implicit context: Context, sortState: SortState)(
|
||||||
length: Smi)
|
length: Smi) {
|
||||||
labels Bailout {
|
|
||||||
// TODO(szuend): Build fast-path that simply installs the work array as the
|
// TODO(szuend): Build fast-path that simply installs the work array as the
|
||||||
// new backing store where applicable.
|
// new backing store where applicable.
|
||||||
const storeFn = sortState.storeFn;
|
let storeFn = sortState.storeFn;
|
||||||
const workArray = sortState.workArray;
|
const workArray = sortState.workArray;
|
||||||
|
|
||||||
for (let i: Smi = 0; i < length; ++i) {
|
for (let i: Smi = 0; i < length; ++i) {
|
||||||
CallStore(storeFn, i, workArray.objects[i]) otherwise Bailout;
|
try {
|
||||||
|
CallStore(storeFn, i, workArray.objects[i]) otherwise Bailout;
|
||||||
|
}
|
||||||
|
label Bailout deferred {
|
||||||
|
sortState.ResetToGenericAccessor();
|
||||||
|
storeFn = sortState.storeFn;
|
||||||
|
CallStore(storeFn, i, workArray.objects[i]) otherwise unreachable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transitioning builtin
|
transitioning builtin
|
||||||
ArrayTimSort(context: Context, sortState: SortState, length: Smi): Object {
|
ArrayTimSort(context: Context, sortState: SortState, length: Smi): Object {
|
||||||
try {
|
CopyReceiverElementsToWorkArray(length);
|
||||||
CopyReceiverElementsToWorkArray(length) otherwise Slow;
|
ArrayTimSortImpl(context, sortState, length);
|
||||||
ArrayTimSortImpl(context, sortState, length);
|
|
||||||
|
|
||||||
|
try {
|
||||||
// The comparison function or toString might have changed the
|
// The comparison function or toString might have changed the
|
||||||
// receiver, if that is the case, we switch to the slow path.
|
// receiver, if that is the case, we switch to the slow path.
|
||||||
// TODO(szuend): Introduce "special" slow path that only copies,
|
|
||||||
// but skips the whole re-sorting.
|
|
||||||
sortState.CheckAccessor() otherwise Slow;
|
sortState.CheckAccessor() otherwise Slow;
|
||||||
CopyWorkArrayToReceiver(length) otherwise Slow;
|
|
||||||
}
|
}
|
||||||
label Slow {
|
label Slow deferred {
|
||||||
if (sortState.accessor == kGenericElementsAccessorId) {
|
sortState.ResetToGenericAccessor();
|
||||||
// We were already on the slow path. This must not happen.
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
const newSortState: SortState = NewSortState(
|
|
||||||
sortState.receiver, sortState.userCmpFn,
|
|
||||||
sortState.initialReceiverLength, sortState.workArray.length, true);
|
|
||||||
ArrayTimSort(context, newSortState, length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CopyWorkArrayToReceiver(length);
|
||||||
return kSuccess;
|
return kSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user