Commit e309b2d9 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[builtins] Port %TypedArray%.prototype.set to Torque

... in an uintptr friendly way.

Drive-by-fix:
1) IsForceSlowPath() check is integrated into Cast<FastJSArray>
2) disable tests incompatible with --force-slow-path in "slow_path" variant

Bug: v8:8906, v8:4153
Change-Id: I427f117073bc295120aa52fb3fe023ee04d58302
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1899988
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64799}
parent d5303952
......@@ -1001,6 +1001,7 @@ torque_files = [
"src/builtins/typed-array-of.tq",
"src/builtins/typed-array-reduce.tq",
"src/builtins/typed-array-reduceright.tq",
"src/builtins/typed-array-set.tq",
"src/builtins/typed-array-slice.tq",
"src/builtins/typed-array-some.tq",
"src/builtins/typed-array-subarray.tq",
......
......@@ -1419,6 +1419,11 @@ const kArrayBufferMaxByteLength:
constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength';
const kMaxTypedArrayInHeap:
constexpr int31 generates 'JSTypedArray::kMaxSizeInHeap';
// CSA does not support 64-bit types on 32-bit platforms so as a workaround the
// kMaxSafeIntegerUint64 is defined as uintptr and allowed to be used only
// inside if constexpr (Is64()) i.e. on 64-bit architectures.
const kMaxSafeIntegerUint64: constexpr uintptr
generates 'static_cast<uintptr_t>(kMaxSafeIntegerUint64)';
const kMaxSafeInteger: constexpr float64 generates 'kMaxSafeInteger';
const kMaxUInt32Double: constexpr float64 generates 'kMaxUInt32Double';
const kSmiMaxValue: constexpr uintptr generates 'kSmiMaxValue';
......@@ -1463,6 +1468,10 @@ const kInvalidDataViewAccessorOffset: constexpr MessageTemplate
generates 'MessageTemplate::kInvalidDataViewAccessorOffset';
const kStrictReadOnlyProperty: constexpr MessageTemplate
generates 'MessageTemplate::kStrictReadOnlyProperty';
const kTypedArraySetOffsetOutOfBounds: constexpr MessageTemplate
generates 'MessageTemplate::kTypedArraySetOffsetOutOfBounds';
const kInvalidArgument: constexpr MessageTemplate
generates 'MessageTemplate::kInvalidArgument';
const kString: constexpr PrimitiveType
generates 'PrimitiveType::kString';
......@@ -2036,6 +2045,9 @@ extern macro IsElementsKindLessThanOrEqual(
ElementsKind, constexpr ElementsKind): bool;
extern macro IsElementsKindGreaterThan(
ElementsKind, constexpr ElementsKind): bool;
extern macro IsElementsKindInRange(
ElementsKind, constexpr ElementsKind, constexpr ElementsKind): bool;
extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;
......@@ -2694,6 +2706,8 @@ Cast<FastJSRegExp>(implicit context: Context)(o: HeapObject): FastJSRegExp
Cast<FastJSArray>(implicit context: Context)(o: HeapObject): FastJSArray
labels CastError {
if (IsForceSlowPath()) goto CastError;
const map: Map = o.map;
if (!IsJSArrayMap(map)) goto CastError;
......@@ -3727,6 +3741,28 @@ macro SameValue(a: JSAny, b: JSAny): bool {
BranchIfSameValue(a, b) otherwise return true, return false;
}
// Does "if (index1 + index2 > limit) goto IfOverflow" in an uintptr overflow
// friendly way where index1 and index2 are in [0, kMaxSafeInteger] range.
macro CheckIntegerIndexAdditionOverflow(
index1: uintptr, index2: uintptr, limit: uintptr) labels IfOverflow {
if constexpr (Is64()) {
assert(index1 <= kMaxSafeIntegerUint64);
assert(index2 <= kMaxSafeIntegerUint64);
// Given that both index1 and index2 are in a safe integer range the
// addition can't overflow.
if (index1 + index2 > limit) goto IfOverflow;
} else {
// Uintptr range is "smaller" than [0, kMaxSafeInteger] range, so
// "index1 + index2" may overflow, so we check the condition in the
// following way "if (index1 > limit - index2) goto IfOverflow" and check
// that "limit - index2" does not underflow.
const index1Limit = limit - index2;
if (index1 > index1Limit) goto IfOverflow;
// Handle potential index1Limit underflow.
if (index1Limit > limit) goto IfOverflow;
}
}
// TODO(tebbi): Define enum here once they appear in Torque.
//
// The value is a SafeInteger that fits into uintptr range, so no bounds checks
......@@ -3808,6 +3844,14 @@ macro ChangeSafeIntegerNumberToUintPtr(value: Number):
}
}
transitioning macro ToUintPtr(implicit context: Context)(value: JSAny):
uintptr labels IfLessThanZero, IfUIntPtrOverflow, IfSafeIntegerOverflow {
if (value == Undefined) return 0;
const indexNumber = ToInteger_Inline(context, value, kTruncateMinusZero);
return TryNumberToUintPtr(indexNumber, kModeValueIsAnyNumber)
otherwise IfLessThanZero, IfUIntPtrOverflow, IfSafeIntegerOverflow;
}
// https://tc39.github.io/ecma262/#sec-toindex
// Unlike ToIndex from the spec this implementation triggers IfRangeError if
// the result is bigger than min(kMaxUIntPtr, kMaxSafeInteger).
......
......@@ -932,8 +932,6 @@ namespace internal {
CPP(TypedArrayPrototypeLastIndexOf) \
/* ES6 #sec-%typedarray%.prototype.reverse */ \
CPP(TypedArrayPrototypeReverse) \
/* ES6 %TypedArray%.prototype.set */ \
TFJ(TypedArrayPrototypeSet, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-get-%typedarray%.prototype-@@tostringtag */ \
TFJ(TypedArrayPrototypeToStringTag, 0, kReceiver) \
/* ES6 %TypedArray%.prototype.map */ \
......
......@@ -168,19 +168,19 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
}
TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
TNode<Word32T> kind) {
TNode<Int32T> kind) {
return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
}
TNode<BoolT> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
TNode<Word32T> kind) {
return Word32Or(Word32Equal(kind, Int32Constant(BIGINT64_ELEMENTS)),
Word32Equal(kind, Int32Constant(BIGUINT64_ELEMENTS)));
TNode<Int32T> kind) {
STATIC_ASSERT(BIGUINT64_ELEMENTS + 1 == BIGINT64_ELEMENTS);
return IsElementsKindInRange(kind, BIGUINT64_ELEMENTS, BIGINT64_ELEMENTS);
}
TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
TNode<Word32T> elements_kind) {
TNode<Int32T> elements_kind) {
TVARIABLE(IntPtrT, element_size);
DispatchTypedArrayByElementsKind(
......@@ -264,127 +264,6 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
return CAST(obj);
}
void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
Label* if_source_too_large) {
CSA_ASSERT(this, Word32BinaryNot(
IsDetachedBuffer(LoadJSArrayBufferViewBuffer(source))));
CSA_ASSERT(this, Word32BinaryNot(
IsDetachedBuffer(LoadJSArrayBufferViewBuffer(target))));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
// Check for possible range errors.
TNode<IntPtrT> source_length = Signed(LoadJSTypedArrayLength(source));
TNode<IntPtrT> target_length = Signed(LoadJSTypedArrayLength(target));
TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset);
GotoIf(IntPtrGreaterThan(required_target_length, target_length),
if_source_too_large);
// Grab pointers and byte lengths we need later on.
TNode<RawPtrT> target_data_ptr = LoadJSTypedArrayDataPtr(target);
TNode<RawPtrT> source_data_ptr = LoadJSTypedArrayDataPtr(source);
TNode<Int32T> source_el_kind = LoadElementsKind(source);
TNode<Int32T> target_el_kind = LoadElementsKind(target);
TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind);
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
// A note on byte lengths: both source- and target byte lengths must be valid,
// i.e. it must be possible to allocate an array of the given length. That
// means we're safe from overflows in the following multiplication.
TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
CSA_ASSERT(this,
UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
Label call_memmove(this), fast_c_call(this), out(this), exception(this);
// A fast memmove call can be used when the source and target types are are
// the same or either Uint8 or Uint8Clamped.
GotoIf(Word32Equal(source_el_kind, target_el_kind), &call_memmove);
GotoIfNot(IsUint8ElementsKind(source_el_kind), &fast_c_call);
Branch(IsUint8ElementsKind(target_el_kind), &call_memmove, &fast_c_call);
BIND(&call_memmove);
{
TNode<RawPtrT> target_start =
RawPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCMemmove(target_start, source_data_ptr, Unsigned(source_byte_length));
Goto(&out);
}
BIND(&fast_c_call);
{
CSA_ASSERT(
this, UintPtrGreaterThanOrEqual(
IntPtrMul(target_length, target_el_size), IntPtrConstant(0)));
GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
IsBigInt64ElementsKind(target_el_kind)),
&exception);
TNode<IntPtrT> source_length = Signed(LoadJSTypedArrayLength(source));
CallCCopyTypedArrayElementsToTypedArray(source, target, source_length,
offset);
Goto(&out);
}
BIND(&exception);
ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
BIND(&out);
}
void TypedArrayBuiltinsAssembler::SetJSArraySource(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
CSA_ASSERT(this, IsFastJSArray(source, context));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
TNode<IntPtrT> target_length = Signed(LoadJSTypedArrayLength(target));
// Maybe out of bounds?
GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
if_source_too_large);
// Nothing to do if {source} is empty.
Label out(this), fast_c_call(this);
GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
// Dispatch based on the source elements kind.
{
// These are the supported elements kinds in TryCopyElementsFastNumber.
int32_t values[] = {
PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
HOLEY_DOUBLE_ELEMENTS,
};
Label* labels[] = {
&fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
};
STATIC_ASSERT(arraysize(values) == arraysize(labels));
TNode<Int32T> source_elements_kind = LoadElementsKind(source);
Switch(source_elements_kind, call_runtime, values, labels,
arraysize(values));
}
BIND(&fast_c_call);
GotoIf(IsBigInt64ElementsKind(LoadElementsKind(target)), call_runtime);
CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
source_length, offset);
Goto(&out);
BIND(&out);
}
void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<RawPtrT> dest_ptr,
TNode<RawPtrT> src_ptr,
TNode<UintPtrT> byte_length) {
......@@ -419,11 +298,9 @@ void TypedArrayBuiltinsAssembler::CallCMemset(TNode<RawPtrT> dest_ptr,
}
void TypedArrayBuiltinsAssembler::
CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
TNode<JSArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length,
TNode<IntPtrT> offset) {
CallCCopyFastNumberJSArrayElementsToTypedArray(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
TNode<UintPtrT> source_length, TNode<UintPtrT> offset) {
CSA_ASSERT(this,
Word32BinaryNot(IsBigInt64ElementsKind(LoadElementsKind(dest))));
TNode<ExternalReference> f = ExternalConstant(
......@@ -438,7 +315,7 @@ void TypedArrayBuiltinsAssembler::
void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
TNode<JSTypedArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset) {
TNode<UintPtrT> source_length, TNode<UintPtrT> offset) {
TNode<ExternalReference> f = ExternalConstant(
ExternalReference::copy_typed_array_elements_to_typed_array());
CallCFunction(f, MachineType::AnyTagged(),
......@@ -592,84 +469,6 @@ void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
StoreElement(data_ptr, elements_kind, index, prepared_value);
}
// ES #sec-get-%typedarray%.prototype.set
TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.set";
TNode<Int32T> argc =
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
CodeStubArguments args(this, argc);
Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
if_offset_is_out_of_bounds(this, Label::kDeferred),
if_source_too_large(this, Label::kDeferred),
if_receiver_is_not_typedarray(this, Label::kDeferred);
// Check the receiver is a typed array.
TNode<Object> receiver = args.GetReceiver();
GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray);
GotoIfNot(IsJSTypedArray(CAST(receiver)), &if_receiver_is_not_typedarray);
// Normalize offset argument (using ToInteger) and handle heap number cases.
TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0));
TNode<Number> offset_num =
ToInteger_Inline(context, offset, kTruncateMinusZero);
// Since ToInteger always returns a Smi if the given value is within Smi
// range, and the only corner case of -0.0 has already been truncated to 0.0,
// we can simply throw unless the offset is a non-negative Smi.
// TODO(jgruber): It's an observable spec violation to throw here if
// {offset_num} is a positive number outside the Smi range. Per spec, we need
// to check for detached buffers and call the observable ToObject/ToLength
// operations first.
GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds);
TNode<Smi> offset_smi = CAST(offset_num);
// Check the receiver is not detached.
ThrowIfArrayBufferViewBufferIsDetached(context, CAST(receiver), method_name);
// Check the source argument is valid and whether a fast path can be taken.
Label call_runtime(this);
TNode<Object> source = args.GetOptionalArgumentValue(0);
GotoIf(TaggedIsSmi(source), &call_runtime);
GotoIf(IsJSTypedArray(CAST(source)), &if_source_is_typed_array);
BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
&call_runtime);
// Fast path for a typed array source argument.
BIND(&if_source_is_typed_array);
{
// Check the source argument is not detached.
ThrowIfArrayBufferViewBufferIsDetached(context, CAST(source), method_name);
SetTypedArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime,
&if_source_too_large);
args.PopAndReturn(UndefinedConstant());
}
// Fast path for a fast JSArray source argument.
BIND(&if_source_is_fast_jsarray);
{
SetJSArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
args.PopAndReturn(UndefinedConstant());
}
BIND(&call_runtime);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
source, offset_smi));
BIND(&if_offset_is_out_of_bounds);
ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
BIND(&if_source_too_large);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
BIND(&if_receiver_is_not_typedarray);
ThrowTypeError(context, MessageTemplate::kNotTypedArray);
}
// ES #sec-get-%typedarray%.prototype-@@tostringtag
TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
......
......@@ -35,13 +35,13 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<UintPtrT> byte_offset);
// Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS.
TNode<BoolT> IsUint8ElementsKind(TNode<Word32T> kind);
TNode<BoolT> IsUint8ElementsKind(TNode<Int32T> kind);
// Returns true if kind is either BIGINT64_ELEMENTS or BIGUINT64_ELEMENTS.
TNode<BoolT> IsBigInt64ElementsKind(TNode<Word32T> kind);
TNode<BoolT> IsBigInt64ElementsKind(TNode<Int32T> kind);
// Returns the byte size of an element for a TypedArray elements kind.
TNode<IntPtrT> GetTypedArrayElementSize(TNode<Word32T> elements_kind);
TNode<IntPtrT> GetTypedArrayElementSize(TNode<Int32T> elements_kind);
// Returns information (byte size and map) about a TypedArray's elements.
ElementsInfo GetTypedArrayElementsInfo(TNode<JSTypedArray> typed_array);
......@@ -57,16 +57,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Object> obj,
const char* method_name);
// Fast path for setting a TypedArray (source) onto another TypedArray
// (target) at an element offset.
void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
void SetJSArraySource(TNode<Context> context, TNode<JSArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
void CallCMemmove(TNode<RawPtrT> dest_ptr, TNode<RawPtrT> src_ptr,
TNode<UintPtrT> byte_length);
......@@ -78,12 +68,12 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
void CallCCopyFastNumberJSArrayElementsToTypedArray(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset);
TNode<UintPtrT> source_length, TNode<UintPtrT> offset);
void CallCCopyTypedArrayElementsToTypedArray(TNode<JSTypedArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length,
TNode<IntPtrT> offset);
TNode<UintPtrT> source_length,
TNode<UintPtrT> offset);
void CallCCopyTypedArrayElementsSlice(TNode<JSTypedArray> source,
TNode<JSTypedArray> dest,
......
......@@ -390,21 +390,8 @@ namespace data_view {
const elementSize: uintptr = DataViewElementSize(kind);
// 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
if constexpr (Is64()) {
// Given that
// a) getIndex is in [0, kMaxSafeInteger] range
// b) elementSize is in [1, 8] range
// the addition can't overflow.
if (getIndex + elementSize > viewSize) goto RangeError;
} else {
// In order to avoid operating on float64s we deal with uintptr values
// and do two comparisons to handle potential uintptr overflow on
// 32-bit architectures.
const lastPossibleElementOffset: uintptr = viewSize - elementSize;
// Check if lastPossibleElementOffset underflowed.
if (lastPossibleElementOffset > viewSize) goto RangeError;
if (getIndex > lastPossibleElementOffset) goto RangeError;
}
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
otherwise RangeError;
// 11. Let bufferIndex be getIndex + viewOffset.
const bufferIndex: uintptr = getIndex + viewOffset;
......@@ -697,21 +684,8 @@ namespace data_view {
const elementSize: uintptr = DataViewElementSize(kind);
// 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
if constexpr (Is64()) {
// Given that
// a) getIndex is in [0, kMaxSafeInteger] range
// b) elementSize is in [1, 8] range
// the addition can't overflow.
if (getIndex + elementSize > viewSize) goto RangeError;
} else {
// In order to avoid operating on float64s we deal with uintptr values
// and do two comparisons to handle potential uintptr overflow on
// 32-bit architectures.
const lastPossibleElementOffset: uintptr = viewSize - elementSize;
// Check if lastPossibleElementOffset underflowed.
if (lastPossibleElementOffset > viewSize) goto RangeError;
if (getIndex > lastPossibleElementOffset) goto RangeError;
}
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
otherwise RangeError;
// 13. Let bufferIndex be getIndex + viewOffset.
const bufferIndex: uintptr = getIndex + viewOffset;
......
......@@ -355,8 +355,7 @@ namespace typed_array {
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo: typed_array::TypedArrayElementsInfo =
typed_array::GetTypedArrayElementsInfo(map);
const elementsInfo = GetTypedArrayElementsInfo(map);
try {
typeswitch (arg1) {
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set';
extern runtime TypedArraySet(Context, JSTypedArray, Object, Number, Number):
void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray(
Context,
FastJSArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
AttachedJSTypedArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
// %TypedArray%.prototype.set ( overloaded [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset
transitioning javascript builtin
TypedArrayPrototypeSet(js-implicit context: Context, receiver: JSAny)(
...arguments): JSAny {
// Steps 2-8 are the same for
// %TypedArray%.prototype.set ( array [ , offset ] ) and
// %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads.
let target: JSTypedArray;
try {
// 2. Let target be the this value.
// 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
// 4. Assert: target has a [[ViewedArrayBuffer]] internal slot.
target = Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
}
label NotTypedArray deferred {
ThrowTypeError(kNotTypedArray, kBuiltinNameSet);
}
try {
// 5. Let targetOffset be ? ToInteger(offset).
// 6. If targetOffset < 0, throw a RangeError exception.
let targetOffsetOverflowed: bool = false;
let targetOffset: uintptr = 0;
if (arguments.length > 1) {
const offsetArg = arguments[1];
try {
targetOffset = ToUintPtr(offsetArg)
// On values less than zero throw RangeError immediately.
otherwise OffsetOutOfBounds,
// On UintPtr or SafeInteger range overflow throw RangeError after
// performing observable steps to follow the spec.
OffsetOverflow, OffsetOverflow;
}
label OffsetOverflow {
targetOffsetOverflowed = true;
}
} else {
// If the offset argument is not provided then the targetOffset is 0.
}
// 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
// exception.
const utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
const overloadedArg = arguments[0];
try {
// 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the
// overloadedArg has a [[TypedArrayName]] internal slot.
// If it does, the definition in 22.2.3.23.2 applies.
// If it does not, the definition in 22.2.3.23.1 applies.
const typedArray =
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
// Step 9 is not observable, do it later.
// 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
// 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
// exception.
const utypedArray =
typed_array::EnsureAttached(typedArray) otherwise IsDetached;
TypedArrayPrototypeSetTypedArray(
utarget, utypedArray, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
}
label NotTypedArray deferred {
TypedArrayPrototypeSetArray(
utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds, IsDetached;
return Undefined;
}
}
label OffsetOutOfBounds deferred {
ThrowRangeError(kTypedArraySetOffsetOutOfBounds);
}
label IsDetached deferred {
ThrowTypeError(kDetachedOperation, kBuiltinNameSet);
}
}
// %TypedArray%.prototype.set ( array [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
transitioning macro
TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds,
IfDetached {
// Steps 9-13 are not observable, do them later.
// TODO(v8:8906): This ported behaviour is an observable spec violation and
// the comment below seems to be outdated. Consider removing this code.
try {
const _arrayArgNum = Cast<Number>(arrayArg) otherwise NotNumber;
// For number as a first argument, throw TypeError instead of silently
// ignoring the call, so that users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
ThrowTypeError(kInvalidArgument);
}
label NotNumber {
// Proceed to step 14.
}
// 14. Let src be ? ToObject(array).
const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 15. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src);
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 16. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
otherwise IfOffsetOutOfBounds;
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 10. Let targetName be the String value of target.[[TypedArrayName]].
// 11. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
// 12. Let targetType be the Element Type value in Table 62 for
// targetName.
try {
// BigInt typed arrays are not handled by
// CopyFastNumberJSArrayElementsToTypedArray.
if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow;
const fastSrc: FastJSArray = Cast<FastJSArray>(src) otherwise goto IfSlow;
const srcKind: ElementsKind = fastSrc.map.elements_kind;
// CopyFastNumberJSArrayElementsToTypedArray() can be used only with the
// following elements kinds:
// PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
// HOLEY_DOUBLE_ELEMENTS.
if (IsElementsKindInRange(
srcKind, PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS) ||
IsElementsKindInRange(
srcKind, PACKED_DOUBLE_ELEMENTS, HOLEY_DOUBLE_ELEMENTS)) {
const utarget =
typed_array::EnsureAttached(target) otherwise IfDetached;
CallCCopyFastNumberJSArrayElementsToTypedArray(
context, fastSrc, utarget, srcLength, targetOffset);
} else {
goto IfSlow;
}
}
label IfSlow deferred {
TypedArraySet(
context, target, src, srcLengthNum, Convert<Number>(targetOffset));
}
}
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
transitioning macro
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 12-20 are not observable, so we can handle offset overflow
// at step 21 here.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 19. Let srcLength be typedArray.[[ArrayLength]].
const srcLength: uintptr = typedArray.length;
// Steps 12-20 are not observable, so we can do step 21 here.
// 21. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// 12. Let targetName be the String value of target.[[TypedArrayName]].
// 13. Let targetType be the Element Type value in Table 62 for
// targetName.
// 14. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
const targetElementsInfo = GetTypedArrayElementsInfo(target);
// 16. Let srcName be the String value of typedArray.[[TypedArrayName]].
// 17. Let srcType be the Element Type value in Table 62 for srcName.
// 18. Let srcElementSize be the Element Size value specified in
// Table 62 for srcName.
const srcKind: ElementsKind = typedArray.elements_kind;
// const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);
// We skip steps 23-25 because both memmove and
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
// regions.
// 23. If both IsSharedArrayBuffer(srcBuffer) and
// IsSharedArrayBuffer(targetBuffer) are true, then
// 23a. If srcBuffer.[[ArrayBufferData]] and
// targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
// values, let same be true; else let same be false.
// 24. Else, let same be SameValue(srcBuffer, targetBuffer).
// 25. If same is true, then
// a. Let srcByteLength be typedArray.[[ByteLength]].
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset,
// srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known
// to not have any observable side-effects.
// d. Let srcByteIndex be 0.
try {
// Use memmove if possible.
if (srcKind != targetElementsInfo.kind) {
// Uint8/Uint8Clamped elements could still be copied with memmove.
if (!IsUint8ElementsKind(srcKind) ||
!IsUint8ElementsKind(targetElementsInfo.kind)) {
goto IfSlow;
}
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// Source and destination typed arrays have same elements kinds (modulo
// Uint8-Uint8Clamped difference) so we can use targetElementsInfo for
// calculations.
const countBytes: uintptr =
targetElementsInfo.CalculateByteLength(srcLength)
otherwise unreachable;
const startOffset: uintptr =
targetElementsInfo.CalculateByteLength(targetOffset)
otherwise unreachable;
const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);
assert(countBytes <= target.byte_length - startOffset);
assert(countBytes <= typedArray.byte_length);
// 29. If srcType is the same as targetType, then
// a. NOTE: If srcType and targetType are the same, the transfer must
// be performed in a manner that preserves the bit-level encoding of
// the source data.
// b. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8,
// true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8,
// value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
}
label IfSlow deferred {
// 22. If target.[[ContentType]] is not equal to
// typedArray.[[ContentType]], throw a TypeError exception.
if (IsBigInt64ElementsKind(srcKind) !=
IsBigInt64ElementsKind(targetElementsInfo.kind))
deferred {
ThrowTypeError(kBigIntMixedTypes);
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 30. Else,
// a. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
// srcType, true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex,
// targetType, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
CallCCopyTypedArrayElementsToTypedArray(
typedArray, target, srcLength, targetOffset);
}
}
}
......@@ -73,6 +73,8 @@ namespace typed_array {
JSTypedArray): TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):
TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind):
bool;
extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
ElementsKind): bool;
extern macro LoadFixedTypedArrayElementAsTagged(
......
......@@ -1051,7 +1051,8 @@ constexpr uint64_t kHoleNanInt64 =
(static_cast<uint64_t>(kHoleNanUpper32) << 32) | kHoleNanLower32;
// ES6 section 20.1.2.6 Number.MAX_SAFE_INTEGER
constexpr double kMaxSafeInteger = 9007199254740991.0; // 2^53-1
constexpr uint64_t kMaxSafeIntegerUint64 = 9007199254740991; // 2^53-1
constexpr double kMaxSafeInteger = static_cast<double>(kMaxSafeIntegerUint64);
constexpr double kMaxUInt32Double = double{kMaxUInt32};
......
......@@ -4771,6 +4771,8 @@ void CopyFastNumberJSArrayElementsToTypedArray(Address raw_context,
switch (destination.GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype) \
case TYPE##_ELEMENTS: \
/* TODO(v8:4153): handle huge offsets here. */ \
DCHECK_LE(offset, kMaxUInt32); \
CHECK(Type##ElementsAccessor::TryCopyElementsFastNumber( \
context, source, destination, length, static_cast<uint32_t>(offset))); \
break;
......@@ -4790,6 +4792,8 @@ void CopyTypedArrayElementsToTypedArray(Address raw_source,
switch (destination.GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype) \
case TYPE##_ELEMENTS: \
/* TODO(v8:4153): handle huge offsets here. */ \
DCHECK_LE(offset, kMaxUInt32); \
Type##ElementsAccessor::CopyElementsFromTypedArray( \
source, destination, length, static_cast<uint32_t>(offset)); \
break;
......
......@@ -157,7 +157,7 @@ class ElementsAccessor {
virtual Object CopyElements(Handle<Object> source,
Handle<JSObject> destination, size_t length,
uint32_t offset = 0) = 0;
uint32_t offset) = 0;
virtual Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate,
Handle<JSObject> object,
......
......@@ -42,7 +42,7 @@ RUNTIME_FUNCTION(Runtime_TypedArrayCopyElements) {
CHECK(TryNumberToSize(*length_obj, &length));
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, length);
return accessor->CopyElements(source, target, length, 0);
}
RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
......@@ -149,49 +149,25 @@ RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
return *array;
}
// 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
RUNTIME_FUNCTION(Runtime_TypedArraySet) {
HandleScope scope(isolate);
Handle<JSTypedArray> target = args.at<JSTypedArray>(0);
Handle<Object> obj = args.at(1);
Handle<Smi> offset = args.at<Smi>(2);
DCHECK(!target->WasDetached()); // Checked in TypedArrayPrototypeSet.
DCHECK(!obj->IsJSTypedArray()); // Should be handled by CSA.
DCHECK_LE(0, offset->value());
const uint32_t uint_offset = static_cast<uint32_t>(offset->value());
if (obj->IsNumber()) {
// For number as a first argument, throw TypeError
// instead of silently ignoring the call, so that
// users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj,
Object::ToObject(isolate, obj));
Handle<Object> len;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len,
Object::GetProperty(isolate, obj, isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len,
Object::ToLength(isolate, len));
DCHECK_EQ(4, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, source, 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 2);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 3);
if (uint_offset + len->Number() > target->length()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
}
size_t length;
CHECK(TryNumberToSize(*length_obj, &length));
uint32_t int_l;
CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l));
size_t offset;
CHECK(TryNumberToSize(*offset_obj, &offset));
DCHECK_LE(offset, kMaxUInt32);
Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj);
ElementsAccessor* accessor = target->GetElementsAccessor();
return accessor->CopyElements(source, target, int_l, uint_offset);
// TODO(v8:4153): Support huge TypedArrays.
return accessor->CopyElements(source, target, length,
static_cast<uint32_t>(offset));
}
} // namespace internal
......
......@@ -1072,6 +1072,12 @@
# Slow tests.
'regress/regress-crbug-493779': [SKIP],
'string-replace-gc': [SKIP],
}], # variant == slow_path and gc_stress
##############################################################################
['variant == slow_path', {
# Tests that are not compatible with the --force-slow-path mode.
'filter-element-kinds': [SKIP],
}], # variant == slow_path
##############################################################################
......
......@@ -27,7 +27,7 @@ description(
var array = [];
for (var i = 0; i < 20000; ++i)
for (var i = 0; i < 2000; ++i)
array.push(i);
array.sort(function(a, b) {
......
......@@ -150,7 +150,6 @@ namespace array {
let canUseSameAccessorFn: CanUseSameAccessorFn;
try {
if (IsForceSlowPath()) goto Slow;
const a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
// Copy copy-on-write (COW) arrays.
......
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