Commit dea41745 authored by Choongwoo Han's avatar Choongwoo Han Committed by Commit Bot

[typedarray] Optimize TypedArray.prototype.slice

- Use CallCFunction to call C implementation of copying elements for
slice, instead of Runtime call
- Copy elements without allocating handles when copying different types
of typed arrays

Bug: v8:5929
Change-Id: Icd4fed8846542e71b623e600ba9aaac64062f0d4
Reviewed-on: https://chromium-review.googlesource.com/920563
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51415}
parent a65e0076
...@@ -1456,6 +1456,12 @@ ExternalReference ExternalReference::copy_typed_array_elements_to_typed_array( ...@@ -1456,6 +1456,12 @@ ExternalReference ExternalReference::copy_typed_array_elements_to_typed_array(
Redirect(isolate, FUNCTION_ADDR(CopyTypedArrayElementsToTypedArray))); Redirect(isolate, FUNCTION_ADDR(CopyTypedArrayElementsToTypedArray)));
} }
ExternalReference ExternalReference::copy_typed_array_elements_slice(
Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(CopyTypedArrayElementsSlice)));
}
ExternalReference ExternalReference::try_internalize_string_function( ExternalReference ExternalReference::try_internalize_string_function(
Isolate* isolate) { Isolate* isolate) {
return ExternalReference(Redirect( return ExternalReference(Redirect(
......
...@@ -984,6 +984,7 @@ class ExternalReference BASE_EMBEDDED { ...@@ -984,6 +984,7 @@ class ExternalReference BASE_EMBEDDED {
Isolate* isolate); Isolate* isolate);
static ExternalReference copy_typed_array_elements_to_typed_array( static ExternalReference copy_typed_array_elements_to_typed_array(
Isolate* isolate); Isolate* isolate);
static ExternalReference copy_typed_array_elements_slice(Isolate* isolate);
static ExternalReference page_flags(Page* page); static ExternalReference page_flags(Page* page);
......
...@@ -1092,6 +1092,16 @@ void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray( ...@@ -1092,6 +1092,16 @@ void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
offset); offset);
} }
void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice(
TNode<JSTypedArray> source, TNode<JSTypedArray> dest, TNode<IntPtrT> start,
TNode<IntPtrT> end) {
TNode<ExternalReference> f = ExternalConstant(
ExternalReference::copy_typed_array_elements_slice(isolate()));
CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::UintPtr(),
MachineType::UintPtr(), f, source, dest, start, end);
}
void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind( void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) { TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
Label next(this), if_unknown_type(this, Label::kDeferred); Label next(this), if_unknown_type(this, Label::kDeferred);
...@@ -1220,8 +1230,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { ...@@ -1220,8 +1230,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
// ES %TypedArray%.prototype.slice // ES %TypedArray%.prototype.slice
TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) { TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.slice"; const char* method_name = "%TypedArray%.prototype.slice";
Label call_runtime(this), call_memmove(this), if_count_is_not_zero(this), Label call_c(this), call_memmove(this), if_count_is_not_zero(this),
if_typed_array_is_neutered(this, Label::kDeferred); if_typed_array_is_neutered(this, Label::kDeferred),
if_bigint_mixed_types(this, Label::kDeferred);
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext)); TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
CodeStubArguments args( CodeStubArguments args(
...@@ -1272,16 +1283,15 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) { ...@@ -1272,16 +1283,15 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
// sharing the same buffer, use memmove. // sharing the same buffer, use memmove.
TNode<Word32T> source_el_kind = LoadElementsKind(source); TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(result_array); TNode<Word32T> target_el_kind = LoadElementsKind(result_array);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_runtime); GotoIfNot(Word32Equal(source_el_kind, target_el_kind), &call_c);
TNode<Object> target_buffer = TNode<Object> target_buffer =
LoadObjectField(result_array, JSTypedArray::kBufferOffset); LoadObjectField(result_array, JSTypedArray::kBufferOffset);
Branch(WordEqual(receiver_buffer, target_buffer), &call_runtime, Branch(WordEqual(receiver_buffer, target_buffer), &call_c, &call_memmove);
&call_memmove);
BIND(&call_memmove); BIND(&call_memmove);
{ {
GotoIfForceSlowPath(&call_runtime); GotoIfForceSlowPath(&call_c);
TNode<IntPtrT> target_data_ptr = TNode<IntPtrT> target_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(result_array)); UncheckedCast<IntPtrT>(LoadDataPtr(result_array));
...@@ -1312,12 +1322,22 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) { ...@@ -1312,12 +1322,22 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
args.PopAndReturn(result_array); args.PopAndReturn(result_array);
} }
BIND(&call_runtime); BIND(&call_c);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySlice, context, source, {
start_index, end_index, result_array)); GotoIf(Word32NotEqual(IsBigInt64ElementsKind(source_el_kind),
IsBigInt64ElementsKind(target_el_kind)),
&if_bigint_mixed_types);
CallCCopyTypedArrayElementsSlice(
source, result_array, SmiToWord(start_index), SmiToWord(end_index));
args.PopAndReturn(result_array);
}
BIND(&if_typed_array_is_neutered); BIND(&if_typed_array_is_neutered);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name); ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
BIND(&if_bigint_mixed_types);
ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
} }
// ES %TypedArray%.prototype.subarray // ES %TypedArray%.prototype.subarray
......
...@@ -116,6 +116,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { ...@@ -116,6 +116,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<IntPtrT> source_length, TNode<IntPtrT> source_length,
TNode<IntPtrT> offset); TNode<IntPtrT> offset);
void CallCCopyTypedArrayElementsSlice(TNode<JSTypedArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> start,
TNode<IntPtrT> end);
typedef std::function<void(ElementsKind, int, int)> TypedArraySwitchCase; typedef std::function<void(ElementsKind, int, int)> TypedArraySwitchCase;
void DispatchTypedArrayByElementsKind( void DispatchTypedArrayByElementsKind(
......
...@@ -721,22 +721,11 @@ class ElementsAccessorBase : public InternalElementsAccessor { ...@@ -721,22 +721,11 @@ class ElementsAccessorBase : public InternalElementsAccessor {
return Subclass::SliceImpl(receiver, start, end); return Subclass::SliceImpl(receiver, start, end);
} }
Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end, Handle<JSObject> result) final {
return Subclass::SliceWithResultImpl(receiver, start, end, result);
}
static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start, static Handle<JSObject> SliceImpl(Handle<JSObject> receiver, uint32_t start,
uint32_t end) { uint32_t end) {
UNREACHABLE(); UNREACHABLE();
} }
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end,
Handle<JSObject> result) {
UNREACHABLE();
}
Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start, Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start,
uint32_t delete_count, Arguments* args, uint32_t delete_count, Arguments* args,
uint32_t add_count) final { uint32_t add_count) final {
...@@ -1038,6 +1027,18 @@ class ElementsAccessorBase : public InternalElementsAccessor { ...@@ -1038,6 +1027,18 @@ class ElementsAccessorBase : public InternalElementsAccessor {
kPackedSizeNotKnown, size); kPackedSizeNotKnown, size);
} }
void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination, size_t start,
size_t end) {
Subclass::CopyTypedArrayElementsSliceImpl(source, destination, start, end);
}
static void CopyTypedArrayElementsSliceImpl(JSTypedArray* source,
JSTypedArray* destination,
size_t start, size_t end) {
UNREACHABLE();
}
Object* CopyElements(Handle<JSReceiver> source, Handle<JSObject> destination, Object* CopyElements(Handle<JSReceiver> source, Handle<JSObject> destination,
size_t length, uint32_t offset) final { size_t length, uint32_t offset) final {
return Subclass::CopyElementsHandleImpl(source, destination, length, return Subclass::CopyElementsHandleImpl(source, destination, length,
...@@ -3201,55 +3202,52 @@ class TypedElementsAccessor ...@@ -3201,55 +3202,52 @@ class TypedElementsAccessor
return result; return result;
} }
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver, static void CopyTypedArrayElementsSliceImpl(JSTypedArray* source,
uint32_t start, uint32_t end, JSTypedArray* destination,
Handle<JSObject> result) { size_t start, size_t end) {
Isolate* isolate = receiver->GetIsolate(); DisallowHeapAllocation no_gc;
DCHECK(!WasNeutered(*receiver)); DCHECK_EQ(destination->GetElementsKind(), AccessorClass::kind());
DCHECK(result->IsJSTypedArray()); DCHECK(!source->WasNeutered());
DCHECK(!WasNeutered(*result)); DCHECK(!destination->WasNeutered());
DCHECK_LE(start, end); DCHECK_LE(start, end);
DCHECK_LE(end, source->length_value());
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver); size_t count = end - start;
Handle<JSTypedArray> result_array = Handle<JSTypedArray>::cast(result); DCHECK_LE(count, destination->length_value());
DCHECK_LE(end, array->length_value());
// Fast path for the same type result array FixedTypedArrayBase* src_elements =
if (result_array->type() == array->type()) { FixedTypedArrayBase::cast(source->elements());
int64_t element_size = array->element_size(); BackingStore* dest_elements = BackingStore::cast(destination->elements());
int64_t count = end - start;
DisallowHeapAllocation no_gc; size_t element_size = source->element_size();
BackingStore* src_elements = BackingStore::cast(receiver->elements()); uint8_t* source_data =
BackingStore* result_elements = static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;
BackingStore::cast(result_array->elements());
// Fast path for the same type result array
DCHECK_LE(count, result_elements->length()); if (source->type() == destination->type()) {
uint8_t* src = uint8_t* dest_data = static_cast<uint8_t*>(dest_elements->DataPtr());
static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;
uint8_t* result = static_cast<uint8_t*>(result_elements->DataPtr()); // The spec defines the copy-step iteratively, which means that we
if (array->buffer() != result_array->buffer()) { // cannot use memcpy if the buffer is shared.
std::memcpy(result, src, count * element_size); uint8_t* end_ptr = source_data + count * element_size;
} else { while (source_data < end_ptr) {
// The spec defines the copy-step iteratively, which means that we *dest_data++ = *source_data++;
// cannot use memcpy if the buffer is shared.
uint8_t* end = src + count * element_size;
while (src < end) {
*result++ = *src++;
}
} }
return result_array; return;
} }
// If the types of the two typed arrays are different, properly convert switch (source->GetElementsKind()) {
// elements #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
Handle<BackingStore> from(BackingStore::cast(array->elements()), isolate); case TYPE##_ELEMENTS: \
ElementsAccessor* result_accessor = result_array->GetElementsAccessor(); CopyBetweenBackingStores<Type##ArrayTraits>(source_data, dest_elements, \
for (uint32_t i = start; i < end; i++) { count, 0); \
Handle<Object> elem = AccessorClass::GetImpl(isolate, *from, i); break;
result_accessor->Set(result_array, i - start, *elem); TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
default:
UNREACHABLE();
break;
} }
return result_array;
} }
static bool HasSimpleRepresentation(InstanceType type) { static bool HasSimpleRepresentation(InstanceType type) {
...@@ -4504,6 +4502,13 @@ void CopyTypedArrayElementsToTypedArray(JSTypedArray* source, ...@@ -4504,6 +4502,13 @@ void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
} }
} }
void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination, uintptr_t start,
uintptr_t end) {
destination->GetElementsAccessor()->CopyTypedArrayElementsSlice(
source, destination, start, end);
}
void ElementsAccessor::InitializeOncePerProcess() { void ElementsAccessor::InitializeOncePerProcess() {
static ElementsAccessor* accessor_array[] = { static ElementsAccessor* accessor_array[] = {
#define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind), #define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind),
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class JSTypedArray;
// Abstract base class for handles that can operate on objects with differing // Abstract base class for handles that can operate on objects with differing
// ElementsKinds. // ElementsKinds.
class ElementsAccessor { class ElementsAccessor {
...@@ -141,9 +143,6 @@ class ElementsAccessor { ...@@ -141,9 +143,6 @@ class ElementsAccessor {
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start, virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end) = 0; uint32_t end) = 0;
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
uint32_t end, Handle<JSObject> result) = 0;
virtual Handle<JSArray> Splice(Handle<JSArray> receiver, virtual Handle<JSArray> Splice(Handle<JSArray> receiver,
uint32_t start, uint32_t delete_count, uint32_t start, uint32_t delete_count,
Arguments* args, uint32_t add_count) = 0; Arguments* args, uint32_t add_count) = 0;
...@@ -193,6 +192,10 @@ class ElementsAccessor { ...@@ -193,6 +192,10 @@ class ElementsAccessor {
Handle<JSObject> object, Handle<JSObject> object,
uint32_t length) = 0; uint32_t length) = 0;
virtual void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination,
size_t start, size_t end) = 0;
protected: protected:
friend class LookupIterator; friend class LookupIterator;
...@@ -241,7 +244,6 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements( ...@@ -241,7 +244,6 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements(
Arguments* args); Arguments* args);
// Called directly from CSA. // Called directly from CSA.
class JSTypedArray;
void CopyFastNumberJSArrayElementsToTypedArray(Context* context, void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source, JSArray* source,
JSTypedArray* destination, JSTypedArray* destination,
...@@ -250,6 +252,9 @@ void CopyFastNumberJSArrayElementsToTypedArray(Context* context, ...@@ -250,6 +252,9 @@ void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
void CopyTypedArrayElementsToTypedArray(JSTypedArray* source, void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
JSTypedArray* destination, JSTypedArray* destination,
uintptr_t length, uintptr_t offset); uintptr_t length, uintptr_t offset);
void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination, uintptr_t start,
uintptr_t end);
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -281,6 +281,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) { ...@@ -281,6 +281,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
Add(ExternalReference::copy_typed_array_elements_to_typed_array(isolate) Add(ExternalReference::copy_typed_array_elements_to_typed_array(isolate)
.address(), .address(),
"copy_typed_array_elements_to_typed_array"); "copy_typed_array_elements_to_typed_array");
Add(ExternalReference::copy_typed_array_elements_slice(isolate).address(),
"copy_typed_array_elements_slice");
Add(ExternalReference::log_enter_external_function(isolate).address(), Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal"); "Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(), Add(ExternalReference::log_leave_external_function(isolate).address(),
......
...@@ -192,29 +192,5 @@ RUNTIME_FUNCTION(Runtime_TypedArraySet) { ...@@ -192,29 +192,5 @@ RUNTIME_FUNCTION(Runtime_TypedArraySet) {
return accessor->CopyElements(source, target, int_l, uint_offset); return accessor->CopyElements(source, target, int_l, uint_offset);
} }
// 22.2.3.4 %TypedArray%.prototype.slice ( start, end )
RUNTIME_FUNCTION(Runtime_TypedArraySlice) {
HandleScope scope(isolate);
Handle<JSTypedArray> source = args.at<JSTypedArray>(0);
Handle<Smi> start = args.at<Smi>(1);
Handle<Smi> end = args.at<Smi>(2);
Handle<JSTypedArray> result = args.at<JSTypedArray>(3);
DCHECK(!source->WasNeutered());
DCHECK(!result->WasNeutered());
DCHECK_LE(start->value(), end->value());
ElementsKind source_kind = source->GetElementsKind();
ElementsKind result_kind = result->GetElementsKind();
if ((source_kind == BIGINT64_ELEMENTS || source_kind == BIGUINT64_ELEMENTS) !=
(result_kind == BIGINT64_ELEMENTS || result_kind == BIGUINT64_ELEMENTS)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kBigIntMixedTypes));
}
ElementsAccessor* accessor = source->GetElementsAccessor();
return *accessor->Slice(source, start->value(), end->value(), result);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -643,7 +643,6 @@ namespace internal { ...@@ -643,7 +643,6 @@ namespace internal {
F(TypedArrayGetBuffer, 1, 1) \ F(TypedArrayGetBuffer, 1, 1) \
F(TypedArraySortFast, 1, 1) \ F(TypedArraySortFast, 1, 1) \
F(TypedArraySet, 2, 1) \ F(TypedArraySet, 2, 1) \
F(TypedArraySlice, 4, 1) \
F(IsTypedArray, 1, 1) F(IsTypedArray, 1, 1)
#define FOR_EACH_INTRINSIC_WASM(F) \ #define FOR_EACH_INTRINSIC_WASM(F) \
......
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