[builtins] Use uintptr for iteration in TypedArray builtins, pt.1

The CL fixes the following builtins:
  %TypedArray%.prototype.join
  %TypedArray%.prototype.every
  %TypedArray%.prototype.find
  %TypedArray%.prototype.findIndex
  %TypedArray%.prototype.forEach
  %TypedArray%.prototype.reduce
  %TypedArray%.prototype.reduceRight
  %TypedArray%.prototype.some

Bug: v8:4153
Change-Id: I39cdb1801949b1df9d221988b8ed4ed5b2de9341
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1864941
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64345}
This commit is contained in:
Igor Sheludko 2019-10-17 11:56:57 +02:00 committed by Commit Bot
parent dcc8b437c7
commit 581bf00eb2
14 changed files with 121 additions and 98 deletions

View File

@ -3,7 +3,7 @@
// found in the LICENSE file.
namespace array_join {
type LoadJoinElementFn = builtin(Context, JSReceiver, Number) => JSAny;
type LoadJoinElementFn = builtin(Context, JSReceiver, uintptr) => JSAny;
// Fast C call to write a fixed array (see Buffer.fixedArray) to a single
// string.
@ -12,20 +12,20 @@ namespace array_join {
FixedArray, intptr, String, String): String;
transitioning builtin LoadJoinElement<T: type>(
context: Context, receiver: JSReceiver, k: Number): JSAny {
return GetProperty(receiver, k);
context: Context, receiver: JSReceiver, k: uintptr): JSAny {
return GetProperty(receiver, Convert<Number>(k));
}
transitioning LoadJoinElement<array::DictionaryElements>(
context: Context, receiver: JSReceiver, k: Number): JSAny {
context: Context, receiver: JSReceiver, k: uintptr): JSAny {
const array: JSArray = UnsafeCast<JSArray>(receiver);
const dict: NumberDictionary = UnsafeCast<NumberDictionary>(array.elements);
try {
return BasicLoadNumberDictionaryElement(dict, Signed(Convert<uintptr>(k)))
return BasicLoadNumberDictionaryElement(dict, Signed(k))
otherwise IfNoData, IfHole;
}
label IfNoData deferred {
return GetProperty(receiver, k);
return GetProperty(receiver, Convert<Number>(k));
}
label IfHole {
return kEmptyString;
@ -33,30 +33,29 @@ namespace array_join {
}
LoadJoinElement<array::FastSmiOrObjectElements>(
context: Context, receiver: JSReceiver, k: Number): JSAny {
context: Context, receiver: JSReceiver, k: uintptr): JSAny {
const array: JSArray = UnsafeCast<JSArray>(receiver);
const fixedArray: FixedArray = UnsafeCast<FixedArray>(array.elements);
const element: Object = fixedArray.objects[UnsafeCast<Smi>(k)];
const element: Object = fixedArray.objects[k];
return element == TheHole ? kEmptyString : UnsafeCast<JSAny>(element);
}
LoadJoinElement<array::FastDoubleElements>(
context: Context, receiver: JSReceiver, k: Number): JSAny {
context: Context, receiver: JSReceiver, k: uintptr): JSAny {
const array: JSArray = UnsafeCast<JSArray>(receiver);
const fixedDoubleArray: FixedDoubleArray =
UnsafeCast<FixedDoubleArray>(array.elements);
const element: float64 = LoadDoubleWithHoleCheck(
fixedDoubleArray, UnsafeCast<Smi>(k)) otherwise return kEmptyString;
const element: float64 = LoadDoubleWithHoleCheck(fixedDoubleArray, k)
otherwise return kEmptyString;
return AllocateHeapNumberWithValue(element);
}
builtin LoadJoinTypedElement<T: type>(
context: Context, receiver: JSReceiver, k: Number): JSAny {
context: Context, receiver: JSReceiver, k: uintptr): JSAny {
const typedArray: JSTypedArray = UnsafeCast<JSTypedArray>(receiver);
assert(!IsDetachedBuffer(typedArray.buffer));
return typed_array::LoadFixedTypedArrayElementAsTagged(
typedArray.data_ptr, UnsafeCast<Smi>(k),
typed_array::KindForArrayType<T>());
typedArray.data_ptr, k, typed_array::KindForArrayType<T>());
}
transitioning builtin ConvertToLocaleString(
@ -283,7 +282,7 @@ namespace array_join {
}
// b. Let element be ? Get(O, ! ToString(k)).
const element: JSAny = loadFn(context, receiver, Convert<Number>(k++));
const element: JSAny = loadFn(context, receiver, k++);
// c. If element is undefined or null, let next be the empty String;
// otherwise, let next be ? ToString(element).

View File

@ -3224,6 +3224,8 @@ extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, Smi): float64
labels IfHole;
extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, intptr): float64
labels IfHole;
extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, uintptr): float64
labels IfHole;
extern macro StoreFixedDoubleArrayHoleSmi(FixedDoubleArray, Smi): void;
macro GetObjectFunction(implicit context: Context)(): JSFunction {

View File

@ -572,7 +572,7 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
TNode<Context> context, TNode<JSTypedArray> typed_array,
TNode<Smi> index_node, TNode<Object> value, ElementsKind elements_kind) {
TNode<UintPtrT> index, TNode<Object> value, ElementsKind elements_kind) {
TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
switch (elements_kind) {
case UINT8_ELEMENTS:
@ -580,27 +580,25 @@ void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
case INT8_ELEMENTS:
case UINT16_ELEMENTS:
case INT16_ELEMENTS:
StoreElement(data_ptr, elements_kind, index_node, SmiToInt32(CAST(value)),
SMI_PARAMETERS);
StoreElement(data_ptr, elements_kind, index, SmiToInt32(CAST(value)));
break;
case UINT32_ELEMENTS:
case INT32_ELEMENTS:
StoreElement(data_ptr, elements_kind, index_node,
TruncateTaggedToWord32(context, value), SMI_PARAMETERS);
StoreElement(data_ptr, elements_kind, index,
TruncateTaggedToWord32(context, value));
break;
case FLOAT32_ELEMENTS:
StoreElement(data_ptr, elements_kind, index_node,
TruncateFloat64ToFloat32(LoadHeapNumberValue(CAST(value))),
SMI_PARAMETERS);
StoreElement(data_ptr, elements_kind, index,
TruncateFloat64ToFloat32(LoadHeapNumberValue(CAST(value))));
break;
case FLOAT64_ELEMENTS:
StoreElement(data_ptr, elements_kind, index_node,
LoadHeapNumberValue(CAST(value)), SMI_PARAMETERS);
StoreElement(data_ptr, elements_kind, index,
LoadHeapNumberValue(CAST(value)));
break;
case BIGUINT64_ELEMENTS:
case BIGINT64_ELEMENTS:
StoreElement(data_ptr, elements_kind, index_node,
UncheckedCast<BigInt>(value), SMI_PARAMETERS);
StoreElement(data_ptr, elements_kind, index,
UncheckedCast<BigInt>(value));
break;
default:
UNREACHABLE();

View File

@ -16,12 +16,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
template <class... TArgs>
TNode<JSTypedArray> TypedArraySpeciesCreate(const char* method_name,
TNode<Context> context,
TNode<JSTypedArray> exemplar,
TArgs... args);
void GenerateTypedArrayPrototypeIterationMethod(TNode<Context> context,
TNode<Object> receiver,
const char* method_name,
@ -120,7 +114,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<UintPtrT> offset);
void StoreJSTypedArrayElementFromTagged(TNode<Context> context,
TNode<JSTypedArray> typed_array,
TNode<Smi> index_node,
TNode<UintPtrT> index_node,
TNode<Object> value,
ElementsKind elements_kind);
};

View File

@ -46,6 +46,10 @@ namespace torque_internal {
return this.TryAtIndex(index) otherwise unreachable;
}
AtIndex(index: uintptr):&T {
return this.TryAtIndex(Convert<intptr>(index)) otherwise unreachable;
}
AtIndex(index: constexpr int31):&T {
const i: intptr = Convert<intptr>(index);
return this.TryAtIndex(i) otherwise unreachable;

View File

@ -11,15 +11,16 @@ namespace typed_array_every {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
for (let k: Smi = 0; k < length; k++) {
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
const result =
Call(context, callbackfn, thisArg, value, k, witness.GetStable());
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (!ToBoolean(result)) {
return False;
}

View File

@ -11,15 +11,16 @@ namespace typed_array_find {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
for (let k: Smi = 0; k < length; k++) {
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
const result =
Call(context, callbackfn, thisArg, value, k, witness.GetStable());
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return value;
}

View File

@ -11,17 +11,19 @@ namespace typed_array_findindex {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Number {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
for (let k: Smi = 0; k < length; k++) {
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
const result =
Call(context, callbackfn, thisArg, value, k, witness.GetStable());
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const indexNumber: Number = Convert<Number>(k);
const result = Call(
context, callbackfn, thisArg, value, indexNumber,
witness.GetStable());
if (ToBoolean(result)) {
return k;
return indexNumber;
}
}
return -1;

View File

@ -11,14 +11,16 @@ namespace typed_array_foreach {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Undefined {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
for (let k: Smi = 0; k < length; k++) {
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
Call(context, callbackfn, thisArg, value, k, witness.GetStable());
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
}
return Undefined;
}

View File

@ -11,11 +11,9 @@ namespace typed_array_reduce {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny | TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: Smi = 0; k < length; k++) {
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
@ -24,9 +22,11 @@ namespace typed_array_reduce {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
witness.GetStable());
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}

View File

@ -11,11 +11,9 @@ namespace typed_array_reduceright {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
initialValue: JSAny | TheHole): JSAny {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
const length: uintptr = witness.Get().length;
let accumulator = initialValue;
for (let k: Smi = length - 1; k >= 0; k--) {
for (let k: uintptr = length; k-- > 0;) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
@ -24,9 +22,11 @@ namespace typed_array_reduceright {
accumulator = value;
}
case (accumulatorNotHole: JSAny): {
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
accumulator = Call(
context, callbackfn, Undefined, accumulatorNotHole, value, k,
witness.GetStable());
context, callbackfn, Undefined, accumulatorNotHole, value,
Convert<Number>(k), witness.GetStable());
}
}
}

View File

@ -11,15 +11,16 @@ namespace typed_array_some {
array: typed_array::AttachedJSTypedArray, callbackfn: Callable,
thisArg: JSAny): Boolean {
let witness = typed_array::NewAttachedJSTypedArrayWitness(array);
// TODO(v8:4153): Support huge TypedArrays here.
const length =
Cast<Smi>(Convert<Number>(witness.Get().length)) otherwise unreachable;
for (let k: Smi = 0; k < length; k++) {
const length: uintptr = witness.Get().length;
for (let k: uintptr = 0; k < length; k++) {
// BUG(4895): We should throw on detached buffers rather than simply exit.
witness.Recheck() otherwise break;
const value: JSAny = witness.Load(k);
const result =
Call(context, callbackfn, thisArg, value, k, witness.GetStable());
// TODO(v8:4153): Consider versioning this loop for Smi and non-Smi
// indices to optimize Convert<Number>(k) for the most common case.
const result = Call(
context, callbackfn, thisArg, value, Convert<Number>(k),
witness.GetStable());
if (ToBoolean(result)) {
return True;
}

View File

@ -70,12 +70,12 @@ namespace typed_array {
extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
ElementsKind): bool;
extern macro LoadFixedTypedArrayElementAsTagged(
RawPtr, Smi, constexpr ElementsKind): Numeric;
RawPtr, uintptr, constexpr ElementsKind): Numeric;
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
Context, JSTypedArray, Smi, JSAny, constexpr ElementsKind);
Context, JSTypedArray, uintptr, JSAny, constexpr ElementsKind);
type LoadFn = builtin(Context, JSTypedArray, Smi) => JSAny;
type StoreFn = builtin(Context, JSTypedArray, Smi, JSAny) => JSAny;
type LoadFn = builtin(Context, JSTypedArray, uintptr) => JSAny;
type StoreFn = builtin(Context, JSTypedArray, uintptr, JSAny) => JSAny;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
JSTypedArray, ByteArray, uintptr): void;
@ -105,11 +105,17 @@ namespace typed_array {
this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable);
}
Load(implicit context: Context)(k: Smi): JSAny {
Load(implicit context: Context)(k: uintptr): JSAny {
const lf: LoadFn = this.loadfn;
return lf(context, this.unstable, k);
}
// TODO(v8:4153): Remove this version once all builtins use uintptr indices
// for iteration.
Load(implicit context: Context)(k: Smi): JSAny {
return this.Load(Unsigned(SmiUntag(k)));
}
stable: JSTypedArray;
unstable: AttachedJSTypedArray;
loadfn: LoadFn;
@ -195,13 +201,13 @@ namespace typed_array {
}
builtin LoadFixedElement<T: type>(
_context: Context, array: JSTypedArray, index: Smi): JSAny {
_context: Context, array: JSTypedArray, index: uintptr): JSAny {
return LoadFixedTypedArrayElementAsTagged(
array.data_ptr, index, KindForArrayType<T>());
}
builtin StoreFixedElement<T: type>(
context: Context, typedArray: JSTypedArray, index: Smi,
context: Context, typedArray: JSTypedArray, index: uintptr,
value: JSAny): JSAny {
typed_array::StoreJSTypedArrayElementFromTagged(
context, typedArray, index, value, KindForArrayType<T>());
@ -232,11 +238,12 @@ namespace typed_array {
transitioning macro
TypedArrayMerge(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
source: FixedArray, from: Smi, middle: Smi, to: Smi, target: FixedArray) {
let left: Smi = from;
let right: Smi = middle;
source: FixedArray, from: uintptr, middle: uintptr, to: uintptr,
target: FixedArray) {
let left: uintptr = from;
let right: uintptr = middle;
for (let targetIndex: Smi = from; targetIndex < to; ++targetIndex) {
for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) {
if (left < middle && right >= to) {
// If the left run has elements, but the right does not, we take
// from the left.
@ -262,17 +269,21 @@ namespace typed_array {
}
transitioning builtin
TypedArrayMergeSort(
implicit context: Context, array: JSTypedArray, comparefn: Callable)(
source: FixedArray, from: Smi, to: Smi, target: FixedArray): JSAny {
TypedArrayMergeSort(implicit context: Context)(
source: FixedArray, from: uintptr, to: uintptr, target: FixedArray,
array: JSTypedArray, comparefn: Callable): JSAny {
assert(to - from > 1);
const middle: Smi = from + ((to - from) >> 1);
const middle: uintptr = from + ((to - from) >>> 1);
// On the next recursion step source becomes target and vice versa.
// This saves the copy of the relevant range from the original
// array into a work array on each recursion step.
if (middle - from > 1) TypedArrayMergeSort(target, from, middle, source);
if (to - middle > 1) TypedArrayMergeSort(target, middle, to, source);
if (middle - from > 1) {
TypedArrayMergeSort(target, from, middle, source, array, comparefn);
}
if (to - middle > 1) {
TypedArrayMergeSort(target, middle, to, source, array, comparefn);
}
TypedArrayMerge(source, from, middle, to, target);
@ -304,8 +315,7 @@ namespace typed_array {
}
// 4. Let len be obj.[[ArrayLength]].
// TODO(v8:4153): Support huge TypedArrays here.
const len = Cast<Smi>(Convert<Number>(array.length)) otherwise unreachable;
const len: uintptr = array.length;
// Arrays of length 1 or less are considered sorted.
if (len < 2) return array;
@ -366,16 +376,16 @@ namespace typed_array {
const work1: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
for (let i: Smi = 0; i < len; ++i) {
for (let i: uintptr = 0; i < len; ++i) {
const element: JSAny = loadfn(context, array, i);
work1.objects[i] = element;
work2.objects[i] = element;
}
TypedArrayMergeSort(work2, 0, len, work1);
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
// work1 contains the sorted numbers. Write them back.
for (let i: Smi = 0; i < len; ++i)
for (let i: uintptr = 0; i < len; ++i)
storefn(context, array, i, UnsafeCast<JSAny>(work1.objects[i]));
return array;

View File

@ -1413,6 +1413,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Float64T> LoadDoubleWithHoleCheck(TNode<FixedDoubleArray> array,
TNode<IntPtrT> index,
Label* if_hole = nullptr);
TNode<Float64T> LoadDoubleWithHoleCheck(TNode<FixedDoubleArray> array,
TNode<UintPtrT> index,
Label* if_hole = nullptr) {
return LoadDoubleWithHoleCheck(array, Signed(index), if_hole);
}
// Load Float64 value by |base| + |offset| address. If the value is a double
// hole then jump to |if_hole|. If |machine_type| is None then only the hole
@ -3251,6 +3256,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// teach TurboFan to handle int64_t on 32-bit platforms eventually.
void StoreElement(Node* elements, ElementsKind kind, Node* index, Node* value,
ParameterMode mode);
void StoreElement(TNode<RawPtrT> elements, ElementsKind kind,
TNode<UintPtrT> index, Node* value) {
return StoreElement(elements, kind, index, value, INTPTR_PARAMETERS);
}
// Implements the BigInt part of
// https://tc39.github.io/proposal-bigint/#sec-numbertorawbytes,