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(
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(
Isolate* isolate) {
return ExternalReference(Redirect(
......
......@@ -984,6 +984,7 @@ class ExternalReference BASE_EMBEDDED {
Isolate* isolate);
static ExternalReference copy_typed_array_elements_to_typed_array(
Isolate* isolate);
static ExternalReference copy_typed_array_elements_slice(Isolate* isolate);
static ExternalReference page_flags(Page* page);
......
......@@ -1092,6 +1092,16 @@ void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
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(
TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function) {
Label next(this), if_unknown_type(this, Label::kDeferred);
......@@ -1220,8 +1230,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
// ES %TypedArray%.prototype.slice
TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.slice";
Label call_runtime(this), call_memmove(this), if_count_is_not_zero(this),
if_typed_array_is_neutered(this, Label::kDeferred);
Label call_c(this), call_memmove(this), if_count_is_not_zero(this),
if_typed_array_is_neutered(this, Label::kDeferred),
if_bigint_mixed_types(this, Label::kDeferred);
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
CodeStubArguments args(
......@@ -1272,16 +1283,15 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
// sharing the same buffer, use memmove.
TNode<Word32T> source_el_kind = LoadElementsKind(source);
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 =
LoadObjectField(result_array, JSTypedArray::kBufferOffset);
Branch(WordEqual(receiver_buffer, target_buffer), &call_runtime,
&call_memmove);
Branch(WordEqual(receiver_buffer, target_buffer), &call_c, &call_memmove);
BIND(&call_memmove);
{
GotoIfForceSlowPath(&call_runtime);
GotoIfForceSlowPath(&call_c);
TNode<IntPtrT> target_data_ptr =
UncheckedCast<IntPtrT>(LoadDataPtr(result_array));
......@@ -1312,12 +1322,22 @@ TF_BUILTIN(TypedArrayPrototypeSlice, TypedArrayBuiltinsAssembler) {
args.PopAndReturn(result_array);
}
BIND(&call_runtime);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySlice, context, source,
start_index, end_index, result_array));
BIND(&call_c);
{
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);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
BIND(&if_bigint_mixed_types);
ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes);
}
// ES %TypedArray%.prototype.subarray
......
......@@ -116,6 +116,11 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<IntPtrT> source_length,
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;
void DispatchTypedArrayByElementsKind(
......
......@@ -721,22 +721,11 @@ class ElementsAccessorBase : public InternalElementsAccessor {
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,
uint32_t end) {
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,
uint32_t delete_count, Arguments* args,
uint32_t add_count) final {
......@@ -1038,6 +1027,18 @@ class ElementsAccessorBase : public InternalElementsAccessor {
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,
size_t length, uint32_t offset) final {
return Subclass::CopyElementsHandleImpl(source, destination, length,
......@@ -3201,55 +3202,52 @@ class TypedElementsAccessor
return result;
}
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end,
Handle<JSObject> result) {
Isolate* isolate = receiver->GetIsolate();
DCHECK(!WasNeutered(*receiver));
DCHECK(result->IsJSTypedArray());
DCHECK(!WasNeutered(*result));
static void CopyTypedArrayElementsSliceImpl(JSTypedArray* source,
JSTypedArray* destination,
size_t start, size_t end) {
DisallowHeapAllocation no_gc;
DCHECK_EQ(destination->GetElementsKind(), AccessorClass::kind());
DCHECK(!source->WasNeutered());
DCHECK(!destination->WasNeutered());
DCHECK_LE(start, end);
DCHECK_LE(end, source->length_value());
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(receiver);
Handle<JSTypedArray> result_array = Handle<JSTypedArray>::cast(result);
DCHECK_LE(end, array->length_value());
size_t count = end - start;
DCHECK_LE(count, destination->length_value());
// Fast path for the same type result array
if (result_array->type() == array->type()) {
int64_t element_size = array->element_size();
int64_t count = end - start;
FixedTypedArrayBase* src_elements =
FixedTypedArrayBase::cast(source->elements());
BackingStore* dest_elements = BackingStore::cast(destination->elements());
DisallowHeapAllocation no_gc;
BackingStore* src_elements = BackingStore::cast(receiver->elements());
BackingStore* result_elements =
BackingStore::cast(result_array->elements());
DCHECK_LE(count, result_elements->length());
uint8_t* src =
static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;
uint8_t* result = static_cast<uint8_t*>(result_elements->DataPtr());
if (array->buffer() != result_array->buffer()) {
std::memcpy(result, src, count * element_size);
} else {
// The spec defines the copy-step iteratively, which means that we
// cannot use memcpy if the buffer is shared.
uint8_t* end = src + count * element_size;
while (src < end) {
*result++ = *src++;
}
size_t element_size = source->element_size();
uint8_t* source_data =
static_cast<uint8_t*>(src_elements->DataPtr()) + start * element_size;
// Fast path for the same type result array
if (source->type() == destination->type()) {
uint8_t* dest_data = static_cast<uint8_t*>(dest_elements->DataPtr());
// The spec defines the copy-step iteratively, which means that we
// cannot use memcpy if the buffer is shared.
uint8_t* end_ptr = source_data + count * element_size;
while (source_data < end_ptr) {
*dest_data++ = *source_data++;
}
return result_array;
return;
}
// If the types of the two typed arrays are different, properly convert
// elements
Handle<BackingStore> from(BackingStore::cast(array->elements()), isolate);
ElementsAccessor* result_accessor = result_array->GetElementsAccessor();
for (uint32_t i = start; i < end; i++) {
Handle<Object> elem = AccessorClass::GetImpl(isolate, *from, i);
result_accessor->Set(result_array, i - start, *elem);
switch (source->GetElementsKind()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
CopyBetweenBackingStores<Type##ArrayTraits>(source_data, dest_elements, \
count, 0); \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
default:
UNREACHABLE();
break;
}
return result_array;
}
static bool HasSimpleRepresentation(InstanceType type) {
......@@ -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() {
static ElementsAccessor* accessor_array[] = {
#define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind),
......
......@@ -12,6 +12,8 @@
namespace v8 {
namespace internal {
class JSTypedArray;
// Abstract base class for handles that can operate on objects with differing
// ElementsKinds.
class ElementsAccessor {
......@@ -141,9 +143,6 @@ class ElementsAccessor {
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
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,
uint32_t start, uint32_t delete_count,
Arguments* args, uint32_t add_count) = 0;
......@@ -193,6 +192,10 @@ class ElementsAccessor {
Handle<JSObject> object,
uint32_t length) = 0;
virtual void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination,
size_t start, size_t end) = 0;
protected:
friend class LookupIterator;
......@@ -241,7 +244,6 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements(
Arguments* args);
// Called directly from CSA.
class JSTypedArray;
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source,
JSTypedArray* destination,
......@@ -250,6 +252,9 @@ void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
void CopyTypedArrayElementsToTypedArray(JSTypedArray* source,
JSTypedArray* destination,
uintptr_t length, uintptr_t offset);
void CopyTypedArrayElementsSlice(JSTypedArray* source,
JSTypedArray* destination, uintptr_t start,
uintptr_t end);
} // namespace internal
} // namespace v8
......
......@@ -281,6 +281,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
Add(ExternalReference::copy_typed_array_elements_to_typed_array(isolate)
.address(),
"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(),
"Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(),
......
......@@ -192,29 +192,5 @@ RUNTIME_FUNCTION(Runtime_TypedArraySet) {
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 v8
......@@ -643,7 +643,6 @@ namespace internal {
F(TypedArrayGetBuffer, 1, 1) \
F(TypedArraySortFast, 1, 1) \
F(TypedArraySet, 2, 1) \
F(TypedArraySlice, 4, 1) \
F(IsTypedArray, 1, 1)
#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