Commit df1676e6 authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[array] Implement Array.p.sort in Torque

This CL implements a generic baseline version and 3 fastpaths, for
various elements kinds, of Array.p.sort in Torque. Details can be found
in the Design Doc: https://goo.gl/Ge321G.

Performance impact on micro benchmarks depends on the element kind
and whether the user provides a comparison function.
For HoleySmi/HoleyElement we have a speedup between 1.5-1.8 across
the board. For Dictionary we are slower in all micro benchmarks (0.7).
For PackedSmi it depends on the call site and whether or not a
comparison function is used.

Detailed numbers: https://goo.gl/mTyPSb

Bug: v8:7382
Change-Id: I50acabd2032af0bc01d36b0de0f555d66be56a7e
Reviewed-on: https://chromium-review.googlesource.com/1061523
Commit-Queue: Simon Zünd <szuend@google.com>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53481}
parent e4046706
......@@ -1716,6 +1716,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
1, false);
SimpleInstallFunction(isolate_, proto, "slice",
Builtins::kArrayPrototypeSlice, 2, false);
SimpleInstallFunction(isolate_, proto, "sort",
Builtins::kArrayPrototypeSort, 1, false);
if (FLAG_enable_experimental_builtins) {
SimpleInstallFunction(isolate_, proto, "splice",
Builtins::kArraySpliceTorque, 2, false);
......
......@@ -466,4 +466,454 @@ module array {
context, kCalledOnNullOrUndefined, 'Array.prototype.forEach');
}
}
// Naming convention from elements.cc. We have a similar intent but implement
// fastpaths using generics instead of using a class hierarchy for elements
// kinds specific implementations.
type GenericElementsAccessor;
type FastPackedSmiElements;
type FastSmiOrObjectElements;
type FastDoubleElements;
macro Load<ElementsAccessor : type>(
context: Context, receiver: Object, index: Smi): Object labels Bailout {
return GetProperty(context, receiver, index);
}
Load<FastPackedSmiElements>(
context: Context, elements: Object, index: Smi): Object labels Bailout {
let elems: FixedArray = unsafe_cast<FixedArray>(elements);
return elems[index];
}
Load<FastSmiOrObjectElements>(
context: Context, elements: Object, index: Smi): Object labels Bailout {
let elems: FixedArray = unsafe_cast<FixedArray>(elements);
let result: Object = elems[index];
if (IsTheHole(result)) goto Bailout;
return result;
}
Load<FastDoubleElements>(
context: Context, elements: Object, index: Smi): Object labels Bailout {
let elems: FixedDoubleArray = unsafe_cast<FixedDoubleArray>(elements);
let value: float64 =
LoadDoubleWithHoleCheck(elems, index) otherwise Bailout;
return AllocateHeapNumberWithValue(value);
}
macro Store<ElementsAccessor : type>(
context: Context, receiver: Object, index: Smi, value: Object) {
SetProperty(context, receiver, index, value, kStrict);
}
Store<FastPackedSmiElements>(
context: Context, elements: Object, index: Smi, value: Object) {
let elems: FixedArray = unsafe_cast<FixedArray>(elements);
elems[index] = value;
}
Store<FastSmiOrObjectElements>(
context: Context, elements: Object, index: Smi, value: Object) {
let elems: FixedArray = unsafe_cast<FixedArray>(elements);
elems[index] = value;
}
Store<FastDoubleElements>(
context: Context, elements: Object, index: Smi, value: Object) {
let elems: FixedDoubleArray = unsafe_cast<FixedDoubleArray>(elements);
let heap_val: HeapNumber = unsafe_cast<HeapNumber>(value);
// Make sure we do not store signalling NaNs into double arrays.
let val: float64 = Float64SilenceNaN(convert<float64>(heap_val));
StoreFixedDoubleArrayElementWithSmiIndex(elems, index, val);
}
type CompareBuiltinFn = builtin(Context, Object, Object, Object) => Number;
builtin SortCompareDefault(
context: Context, comparefn: Object, x: Object, y: Object): Number {
assert(comparefn == Undefined);
if (TaggedIsSmi(x) && TaggedIsSmi(y)) {
// TODO(szuend): Replace with a fast CallCFunction call.
return SmiLexicographicCompare(context, x, y);
}
// 5. Let xString be ? ToString(x).
let xString: String = ToString_Inline(context, x);
// 6. Let yString be ? ToString(y).
let yString: String = ToString_Inline(context, y);
// 7. Let xSmaller be the result of performing
// Abstract Relational Comparison xString < yString.
// 8. If xSmaller is true, return -1.
if (StringLessThan(context, xString, yString) == True) return -1;
// 9. Let ySmaller be the result of performing
// Abstract Relational Comparison yString < xString.
// 10. If ySmaller is true, return 1.
if (StringLessThan(context, yString, xString) == True) return 1;
// 11. Return +0.
return 0;
}
builtin SortCompareUserFn(
context: Context, comparefn: Object, x: Object, y: Object): Number {
assert(comparefn != Undefined);
let cmpfn: Callable = unsafe_cast<Callable>(comparefn);
// a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)).
let v: Number =
ToNumber_Inline(context, Call(context, cmpfn, Undefined, x, y));
// b. If v is NaN, return +0.
if (NumberIsNaN(v)) return 0;
// c. return v.
return v;
}
macro CanUseSameAccessor<ElementsAccessor : type>(
context: Context, receiver: Object, initialReceiverMap: Object,
initialReceiverLength: Number): bool {
assert(IsJSArray(unsafe_cast<HeapObject>(receiver)));
let a: JSArray = unsafe_cast<JSArray>(receiver);
if (a.map != initialReceiverMap) return false;
let originalLength: Smi = unsafe_cast<Smi>(initialReceiverLength);
if (a.length_fast != originalLength) return false;
return true;
}
CanUseSameAccessor<GenericElementsAccessor>(
context: Context, receiver: Object, initialReceiverMap: Object,
initialReceiverLength: Number): bool {
// Do nothing. We are already on the slow path.
return true;
}
macro CallCompareFn<E : type>(
context: Context, receiver: Object, initialReceiverMap: Object,
initialReceiverLength: Number, userCmpFn: Object,
sortCompare: CompareBuiltinFn, x: Object,
y: Object): Number labels Bailout {
let result: Number = sortCompare(context, userCmpFn, x, y);
if (!CanUseSameAccessor<E>(
context, receiver, initialReceiverMap, initialReceiverLength))
goto Bailout;
return result;
}
// InsertionSort is used for smaller arrays.
macro ArrayInsertionSort<E : type>(
context: Context, receiver: Object, elements: Object,
initialReceiverMap: Object, initialReceiverLength: Number, from: Smi,
to: Smi, userCmpFn: Object, sortCompare: CompareBuiltinFn)
labels Bailout {
for (let i: Smi = from + 1; i < to; ++i) {
let element: Object = Load<E>(context, elements, i) otherwise Bailout;
let j: Smi = i - 1;
for (; j >= from; --j) {
// TODO(szuend): Introduce line breaks when multi-line asserts are
// fixed in Torque.
assert(CanUseSameAccessor<E>(context, receiver, initialReceiverMap, initialReceiverLength));
let tmp: Object = Load<E>(context, elements, j) otherwise Bailout;
let order: Number = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength,
userCmpFn, sortCompare, tmp, element)
otherwise Bailout;
if (order > 0) {
Store<E>(context, elements, j + 1, tmp);
} else {
break;
}
}
Store<E>(context, elements, j + 1, element);
}
}
macro CalculatePivot<E : type>(
context: Context, receiver: Object, elements: Object,
initialReceiverMap: Object, initialReceiverLength: Number, from: Smi,
to: Smi, userCmpFn: Object, sortCompare: CompareBuiltinFn): Object
labels Bailout {
// TODO(szuend): Check if a more involved third_index calculation is
// worth it for very large arrays.
let third_index: Smi = from + ((to - from) >>> 1);
// Find a pivot as the median of first, last and middle element.
let v0: Object = Load<E>(context, elements, from) otherwise Bailout;
let v1: Object = Load<E>(context, elements, to - 1) otherwise Bailout;
let v2: Object = Load<E>(context, elements, third_index) otherwise Bailout;
let c01: Number = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength, userCmpFn,
sortCompare, v0, v1)
otherwise Bailout;
if (c01 > 0) {
// v0 > v1, so swap them.
let tmp: Object = v0;
v0 = v1;
v1 = tmp;
}
// Current state: v0 <= v1.
let c02: Number = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength, userCmpFn,
sortCompare, v0, v2)
otherwise Bailout;
if (c02 >= 0) {
// v0 <= v1 and v0 >= v2, hence swap to v2 <= v0 <= v1.
let tmp: Object = v0;
v0 = v2;
v2 = v1;
v1 = tmp;
} else {
// v0 <= v1 and v0 < v2.
let c12: Number = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength,
userCmpFn, sortCompare, v1, v2)
otherwise Bailout;
if (c12 > 0) {
// v0 <= v1 and v0 < v2 and v1 > v2, hence swap to v0 <= v2 < v1.
let tmp: Object = v1;
v1 = v2;
v2 = tmp;
}
}
// v0 <= v1 <= v2.
Store<E>(context, elements, from, v0);
Store<E>(context, elements, to - 1, v2);
// Move pivot element to a place on the left.
Swap<E>(context, elements, from + 1, third_index, v1) otherwise Bailout;
return v1;
}
// elements[indexB] = elements[indexA].
// elements[indexA] = value.
macro Swap<E : type>(
context: Context, elements: Object, indexA: Smi, indexB: Smi,
value: Object)
labels Bailout {
let tmp: Object = Load<E>(context, elements, indexA) otherwise Bailout;
Store<E>(context, elements, indexB, tmp);
Store<E>(context, elements, indexA, value);
}
macro ArrayQuickSortImpl<E : type>(
context: Context, receiver: Object, elements: Object,
initialReceiverMap: Object, initialReceiverLength: Number, fromArg: Smi,
toArg: Smi, userCmpFn: Object, sortCompare: CompareBuiltinFn)
labels Bailout {
let from: Smi = fromArg;
let to: Smi = toArg;
while (to - from > 1) {
if (to - from <= 10) {
ArrayInsertionSort<E>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, from, to, userCmpFn, sortCompare)
otherwise Bailout;
break;
}
let pivot: Object = CalculatePivot<E>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, from, to, userCmpFn, sortCompare)
otherwise Bailout;
let low_end: Smi = from + 1; // Upper bound of elems lower than pivot.
let high_start: Smi = to - 1; // Lower bound of elems greater than pivot.
// From low_end to idx are elements equal to pivot.
// From idx to high_start are elements that haven"t been compared yet.
for (let idx: Smi = low_end + 1; idx < high_start; idx++) {
// TODO(szuend): Introduce line breaks when multi-line asserts are
// fixed in Torque.
assert(CanUseSameAccessor<E>(context, receiver, initialReceiverMap, initialReceiverLength));
let element: Object = Load<E>(context, elements, idx) otherwise Bailout;
let order: Number = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength,
userCmpFn, sortCompare, element, pivot)
otherwise Bailout;
if (order < 0) {
Swap<E>(context, elements, low_end, idx, element) otherwise Bailout;
low_end++;
} else if (order > 0) {
let break_for: bool = false;
// Start looking for high_start to find the first value that is
// smaller than pivot.
while (order > 0) {
high_start--;
if (high_start == idx) {
break_for = true;
break;
}
let top_elem: Object =
Load<E>(context, elements, high_start) otherwise Bailout;
order = CallCompareFn<E>(
context, receiver, initialReceiverMap, initialReceiverLength,
userCmpFn, sortCompare, top_elem, pivot)
otherwise Bailout;
}
if (break_for) {
break;
}
Swap<E>(context, elements, high_start, idx, element)
otherwise Bailout;
if (order < 0) {
element = Load<E>(context, elements, idx) otherwise Bailout;
Swap<E>(context, elements, low_end, idx, element) otherwise Bailout;
low_end++;
}
}
}
if ((to - high_start) < (low_end - from)) {
ArrayQuickSort<E>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, high_start, to, userCmpFn, sortCompare);
to = low_end;
} else {
ArrayQuickSort<E>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, from, low_end, userCmpFn, sortCompare);
from = high_start;
}
}
}
builtin ArrayQuickSort<ElementsAccessor : type>(
context: Context, receiver: Object, elements: Object,
initialReceiverMap: Object, initialReceiverLength: Number, from: Smi,
to: Smi, userCmpFn: Object, sortCompare: CompareBuiltinFn): Object {
try {
ArrayQuickSortImpl<ElementsAccessor>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, from, to, userCmpFn, sortCompare)
otherwise Slow;
}
label Slow {
ArrayQuickSort<GenericElementsAccessor>(
context, receiver, receiver, initialReceiverMap,
initialReceiverLength, from, to, userCmpFn, sortCompare);
}
return receiver;
}
// The specialization is needed since we would end up in an endless loop
// when the ElementsAccessor fails and bails to the ElementsAccessor again.
ArrayQuickSort<GenericElementsAccessor>(
context: Context, receiver: Object, elements: Object,
initialReceiverMap: Object, initialReceiverLength: Number, from: Smi,
to: Smi, userCmpFn: Object, sortCompare: CompareBuiltinFn): Object {
try {
ArrayQuickSortImpl<GenericElementsAccessor>(
context, receiver, elements, initialReceiverMap,
initialReceiverLength, from, to, userCmpFn, sortCompare)
otherwise Error;
}
label Error {
// The generic baseline path must not fail.
unreachable;
}
return receiver;
}
// For compatibility with JSC, we also sort elements inherited from
// the prototype chain on non-Array objects.
// We do this by copying them to this object and sorting only
// own elements. This is not very efficient, but sorting with
// inherited elements happens very, very rarely, if at all.
// The specification allows "implementation dependent" behavior
// if an element on the prototype chain has an element that
// might interact with sorting.
//
// We also move all non-undefined elements to the front of the
// array and move the undefineds after that. Holes are removed.
// This happens for Array as well as non-Array objects.
extern runtime PrepareElementsForSort(Context, Object, Number): Smi;
// https://tc39.github.io/ecma262/#sec-array.prototype.sort
javascript builtin ArrayPrototypeSort(
context: Context, receiver: Object, ...arguments): Object {
// 1. If comparefn is not undefined and IsCallable(comparefn) is false,
// throw a TypeError exception.
let comparefnObj: Object = arguments[0];
if (comparefnObj != Undefined && !TaggedIsCallable(comparefnObj)) {
ThrowTypeError(context, kBadSortComparisonFunction, comparefnObj);
}
// 2. Let obj be ? ToObject(this value).
let obj: Object = ToObject(context, receiver);
let cmpBuiltin: CompareBuiltinFn =
comparefnObj != Undefined ? SortCompareUserFn : SortCompareDefault;
try {
let a: JSArray = cast<JSArray>(obj) otherwise slow;
let map: Map = a.map;
let elementsKind: ElementsKind = map.elements_kind;
if (!IsFastElementsKind(elementsKind)) goto slow;
// 3. Let len be ? ToLength(? Get(obj, "length")).
let len: Smi = a.length_fast;
if (len < 2) return receiver;
// TODO(szuend): Investigate performance tradeoff of skipping this step
// for PACKED_* and handling Undefineds during sorting.
let nofNonUndefined: Smi = PrepareElementsForSort(context, obj, len);
// TODO(szuend): Extract into IsDoubleElementsKind when bool types are
// fixed in Torque.
if (elementsKind == convert<ElementsKind>(PACKED_DOUBLE_ELEMENTS) ||
elementsKind == convert<ElementsKind>(HOLEY_DOUBLE_ELEMENTS)) {
let elements: FixedDoubleArray =
unsafe_cast<FixedDoubleArray>(a.elements);
ArrayQuickSort<FastDoubleElements>(
context, obj, elements, map, len, 0, nofNonUndefined, comparefnObj,
cmpBuiltin);
} else {
let elements: FixedArray = unsafe_cast<FixedArray>(a.elements);
if (elementsKind == convert<ElementsKind>(PACKED_SMI_ELEMENTS)) {
ArrayQuickSort<FastPackedSmiElements>(
context, obj, elements, map, len, 0, nofNonUndefined,
comparefnObj, cmpBuiltin);
} else {
ArrayQuickSort<FastSmiOrObjectElements>(
context, obj, elements, map, len, 0, nofNonUndefined,
comparefnObj, cmpBuiltin);
}
}
}
label slow {
// 3. Let len be ? ToLength(? Get(obj, "length")).
let len: Number =
ToLength_Inline(context, GetProperty(context, obj, 'length'));
if (len < 2) return receiver;
let nofNonUndefined: Smi = PrepareElementsForSort(context, obj, len);
ArrayQuickSort<GenericElementsAccessor>(
context, obj, obj, Undefined, len, 0, nofNonUndefined, comparefnObj,
cmpBuiltin);
}
return receiver;
}
}
......@@ -52,6 +52,7 @@ type ElementsKind generates 'TNode<Int32T>' constexpr 'ElementsKind';
type LanguageMode generates 'TNode<Smi>' constexpr 'LanguageMode';
type ExtractFixedArrayFlags generates
'TNode<Smi>' constexpr 'ExtractFixedArrayFlags';
type ParameterMode generates 'TNode<Int32T>' constexpr 'ParameterMode';
type MessageTemplate constexpr 'MessageTemplate';
type HasPropertyLookupMode constexpr 'HasPropertyLookupMode';
......@@ -127,6 +128,9 @@ const false: constexpr bool = 'false';
const kStrict: constexpr LanguageMode = 'LanguageMode::kStrict';
const kSloppy: constexpr LanguageMode = 'LanguageMode::kSloppy';
const SMI_PARAMETERS: constexpr ParameterMode = 'SMI_PARAMETERS';
const INTPTR_PARAMETERS: constexpr ParameterMode = 'INTPTR_PARAMETERS';
extern macro Print(Object);
extern macro DebugBreak();
extern macro ToInteger_Inline(Context, Object): Number;
......@@ -144,12 +148,19 @@ extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
extern builtin ToObject(Context, Object): Object;
extern macro IsNullOrUndefined(Object): bool;
extern macro IsTheHole(Object): bool;
extern macro IsString(HeapObject): bool;
extern builtin ToString(Context, Object): String;
extern runtime CreateDataProperty(Context, Object, Object, Object);
extern runtime SetProperty(Context, Object, Object, Object, LanguageMode);
extern runtime DeleteProperty(Context, Object, Object, LanguageMode);
extern runtime StringEqual(Context, String, String): Oddball;
extern builtin StringLessThan(Context, String, String): Boolean;
extern macro StrictEqual(Object, Object): Boolean;
extern runtime SmiLexicographicCompare(Context, Object, Object): Number;
extern operator '==' macro Word32Equal(int32, int32): bool;
extern operator '!=' macro Word32NotEqual(int32, int32): bool;
......@@ -227,8 +238,8 @@ extern operator
extern operator 'is<Smi>' macro TaggedIsSmi(Object): bool;
extern operator 'isnt<Smi>' macro TaggedIsNotSmi(Object): bool;
extern operator 'cast<>' macro TaggedToJSDataView(
Object): JSDataView labels CastError;
extern operator
'cast<>' macro TaggedToJSDataView(Object): JSDataView labels CastError;
extern operator
'cast<>' macro TaggedToHeapObject(Object): HeapObject labels CastError;
extern operator 'cast<>' macro TaggedToSmi(Object): Smi labels CastError;
......@@ -266,6 +277,8 @@ extern operator 'convert<>' macro TruncateWordToWord32(intptr): int32;
extern operator 'convert<>' macro SmiTag(intptr): Smi;
extern operator 'convert<>' macro SmiFromInt32(int32): Smi;
extern operator 'convert<>' macro SmiUntag(Smi): intptr;
extern operator 'convert<>' macro LoadHeapNumberValue(HeapNumber): float64;
extern operator 'convert<>' macro ChangeNumberToFloat64(Number): float64;
extern macro BranchIfFastJSArray(Object, Context): never labels Taken, NotTaken;
extern macro BranchIfNotFastJSArray(Object, Context): never labels Taken,
......@@ -291,6 +304,7 @@ extern operator '.elements=' macro StoreElements(JSObject, FixedArrayBase);
extern operator '.length' macro LoadTypedArrayLength(JSTypedArray): Smi;
extern operator '.length' macro LoadJSArrayLength(JSArray): Number;
extern operator '.length_fast' macro LoadFastJSArrayLength(JSArray): Smi;
extern operator '.length=' macro StoreJSArrayLength(JSArray, Smi);
extern operator '.length' macro LoadFixedArrayBaseLength(FixedArrayBase): Smi;
......@@ -301,6 +315,16 @@ extern operator
extern operator
'[]=' macro StoreFixedArrayElementSmi(FixedArray, Smi, Object): void;
extern macro LoadFixedDoubleArrayElement(FixedDoubleArray, Smi): float64;
extern macro Float64SilenceNaN(float64): float64;
extern macro StoreFixedDoubleArrayElement(
FixedDoubleArray, Object, float64, constexpr ParameterMode);
macro StoreFixedDoubleArrayElementWithSmiIndex(
array: FixedDoubleArray, index: Smi, value: float64) {
StoreFixedDoubleArrayElement(array, index, value, SMI_PARAMETERS);
}
extern macro IsFastElementsKind(ElementsKind): bool;
extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
extern macro IsFastSmiElementsKind(ElementsKind): bool;
......@@ -373,5 +397,8 @@ macro HasPropertyObject(
}
extern macro IsCallable(HeapObject): bool;
extern macro IsJSArray(HeapObject): bool;
extern macro TaggedIsCallable(Object): bool;
extern macro IsDetachedBuffer(JSArrayBuffer): bool;
extern macro NumberIsNaN(Number): bool;
......@@ -1199,27 +1199,6 @@ void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
BIND(&next);
}
TNode<BoolT> TypedArrayBuiltinsAssembler::NumberIsNaN(TNode<Number> value) {
Label is_heapnumber(this), done(this);
TVARIABLE(BoolT, result);
GotoIf(TaggedIsNotSmi(value), &is_heapnumber);
result = Int32FalseConstant();
Goto(&done);
BIND(&is_heapnumber);
{
CSA_ASSERT(this, IsHeapNumber(CAST(value)));
TNode<Float64T> value_f = LoadHeapNumberValue(CAST(value));
result = Float64NotEqual(value_f, value_f);
Goto(&done);
}
BIND(&done);
return result.value();
}
// ES #sec-get-%typedarray%.prototype.set
TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
......
......@@ -130,16 +130,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
void DispatchTypedArrayByElementsKind(
TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function);
// Returns true iff number is NaN.
// TOOD(szuend): Remove when UncheckedCasts are supported in Torque.
TNode<BoolT> NumberIsNaN(TNode<Number> number);
// TODO(szuend): Remove when UncheckedCasts are supported in Torque.
TNode<FixedTypedArrayBase> UncheckedCastFixedArrayBaseToFixedTypedArrayBase(
TNode<FixedArrayBase> array) {
return CAST(array);
}
};
} // namespace internal
......
......@@ -9,10 +9,6 @@ module typed_array {
const kTypedArrayProtoSort: MethodName = '\"%TypedArray%.prototype.sort\"';
extern macro ValidateTypedArray(Context, Object, MethodName): JSTypedArray;
extern macro NumberIsNaN(Number): bool;
type ParameterMode constexpr 'ParameterMode';
const SMI_PARAMETERS: constexpr ParameterMode = 'SMI_PARAMETERS';
extern macro LoadFixedTypedArrayElementAsTagged(
RawPtr, Smi, constexpr ElementsKind, constexpr ParameterMode): Object;
extern macro StoreFixedTypedArrayElementFromTagged(
......@@ -57,10 +53,6 @@ module typed_array {
return BIGINT64_ELEMENTS;
}
extern operator
'convert<>' macro UncheckedCastFixedArrayBaseToFixedTypedArrayBase(
FixedArrayBase): FixedTypedArrayBase;
builtin LoadFixedElement<T : type>(
context: Context, array: JSTypedArray, index: Smi): Object {
return LoadFixedTypedArrayElementAsTagged(
......@@ -71,7 +63,7 @@ module typed_array {
context: Context, array: JSTypedArray, index: Smi,
value: Object): Object {
let elements: FixedTypedArrayBase =
convert<FixedTypedArrayBase>(array.elements);
unsafe_cast<FixedTypedArrayBase>(array.elements);
StoreFixedTypedArrayElementFromTagged(
context, elements, index, value, KindForArrayType<T>(), SMI_PARAMETERS);
return Undefined;
......
......@@ -11788,5 +11788,26 @@ void CodeStubAssembler::InitializeFunctionContext(Node* native_context,
native_context);
}
TNode<BoolT> CodeStubAssembler::NumberIsNaN(TNode<Number> value) {
Label is_heapnumber(this), done(this);
TVARIABLE(BoolT, result);
GotoIf(TaggedIsNotSmi(value), &is_heapnumber);
result = Int32FalseConstant();
Goto(&done);
BIND(&is_heapnumber);
{
CSA_ASSERT(this, IsHeapNumber(CAST(value)));
TNode<Float64T> value_f = LoadHeapNumberValue(CAST(value));
result = Float64NotEqual(value_f, value_f);
Goto(&done);
}
BIND(&done);
return result.value();
}
} // namespace internal
} // namespace v8
......@@ -195,6 +195,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
return CAST(result);
}
// Returns true iff number is NaN.
// TOOD(szuend): Remove when UncheckedCasts are supported in Torque.
TNode<BoolT> NumberIsNaN(TNode<Number> number);
Node* MatchesParameterMode(Node* value, ParameterMode mode);
#define PARAMETER_BINOP(OpName, IntPtrOpName, SmiOpName) \
......@@ -798,6 +802,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
ParameterMode parameter_mode = INTPTR_PARAMETERS,
Label* if_hole = nullptr);
Node* LoadFixedDoubleArrayElement(TNode<FixedDoubleArray> object,
TNode<Smi> index) {
return LoadFixedDoubleArrayElement(object, index, MachineType::Float64(), 0,
SMI_PARAMETERS);
}
// Load a feedback slot from a FeedbackVector.
TNode<MaybeObject> LoadFeedbackVectorSlot(
Node* object, Node* index, int additional_offset = 0,
......
......@@ -634,6 +634,7 @@ SharedFunctionInfo::SideEffectState BuiltinGetSideEffectState(
case Builtins::kArrayPrototypeFlatMap:
case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayPrototypeSlice:
case Builtins::kArrayPrototypeSort:
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:
case Builtins::kArraySome:
......
......@@ -665,6 +665,8 @@ function ArraySpliceFallback(start, delete_count) {
}
// TODO(szuend): Remove once last remaining call site in GetSortedArrayKeys
// does not use it anymore.
function InnerArraySort(array, length, comparefn) {
// In-place QuickSort algorithm.
// For short (length <= 10) arrays, insertion sort is used for efficiency.
......@@ -823,19 +825,6 @@ function InnerArraySort(array, length, comparefn) {
}
DEFINE_METHOD(
GlobalArray.prototype,
sort(comparefn) {
if (!IS_UNDEFINED(comparefn) && !IS_CALLABLE(comparefn)) {
throw %make_type_error(kBadSortComparisonFunction, comparefn);
}
var array = TO_OBJECT(this);
var length = TO_LENGTH(array.length);
return InnerArraySort(array, length, comparefn);
}
);
DEFINE_METHOD_LEN(
GlobalArray.prototype,
lastIndexOf(element, index) {
......@@ -1004,7 +993,6 @@ var ArrayPop = GlobalArray.prototype.pop;
var ArrayPush = GlobalArray.prototype.push;
var ArraySlice = GlobalArray.prototype.slice;
var ArrayShift = GlobalArray.prototype.shift;
var ArraySort = GlobalArray.prototype.sort;
var ArraySplice = GlobalArray.prototype.splice;
var ArrayToString = GlobalArray.prototype.toString;
var ArrayUnshift = GlobalArray.prototype.unshift;
......@@ -1026,7 +1014,6 @@ utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
"pop", ArrayPop,
"push", ArrayPush,
"shift", ArrayShift,
"sort", ArraySort,
"splice", ArraySplice
]);
......@@ -1050,7 +1037,6 @@ utils.Export(function(to) {
to.ArrayToString = ArrayToString;
to.ArrayValues = ArrayValues;
to.InnerArrayJoin = InnerArrayJoin;
to.InnerArraySort = InnerArraySort;
to.InnerArrayToLocaleString = InnerArrayToLocaleString;
});
......
......@@ -1245,7 +1245,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("g"));
CHECK(IsCompiled("String.raw"));
CHECK(!IsCompiled("Array.prototype.sort"));
CHECK(!IsCompiled("Array.prototype.lastIndexOf"));
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
}
isolate->Dispose();
......
......@@ -2,5 +2,5 @@
([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]])([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[+!+[]]]((![]+[])[+!+[]])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((![]+[])[+!+[]]+(+[![]]+[])[+[]])[+[]]+(![]+[])[+!+[]]+(+[]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+([]+([]+[])[([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[!+[]+!+[]+!+[]+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((![]+[])[+!+[]]+[+[]])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])+[])[+[]]+(![]+[])[+[]])[+[]])
^
TypeError: Cannot convert undefined or null to object
at sort (native)
at sort (<anonymous>)
at *%(basename)s:34:410
......@@ -778,6 +778,13 @@
'wasm/grow-memory': [SKIP],
}], # variant == nooptimization and (arch == arm or arch == arm64) and simulator_run
##############################################################################
['(arch == arm or arch == arm64) and simulator_run', {
# TODO(szuend): Re-enable when pivot calculation is fixed:
# https://crbug.com/v8/7795
'regress/regress-2185': [SKIP],
}], # (arch == arm or arch == arm64) and simulator_run
##############################################################################
['gcov_coverage', {
# Tests taking too long.
......
......@@ -57,25 +57,8 @@ function testTraceNativeConversion(nativeFunc) {
}
}
function testNotOmittedBuiltin(throwing, included) {
try {
throwing();
assertUnreachable(included);
} catch (e) {
assertTrue(e.stack.indexOf(included) >= 0, included);
}
}
testTraceNativeConversion(String); // Does ToString on argument.
testTraceNativeConversion(RegExp); // Does ToString on argument.
testTraceNativeConstructor(String); // Does ToString on argument.
testTraceNativeConstructor(RegExp); // Does ToString on argument.
// QuickSort has builtins object as receiver, and is non-native
// builtin. Should not be omitted with the --builtins-in-stack-traces flag.
testNotOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) {
(b < a) - (a < b); });
}, "QuickSort");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment