From: dslomov@chromium.org Date: Wed, 31 Jul 2013 12:10:49 +0000 (+0000) Subject: Reimplement TypedArray.set in Javascript. X-Git-Tag: upstream/4.7.83~13112 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=693301702bc7187ad694efbacfae92b942d01e76;p=platform%2Fupstream%2Fv8.git Reimplement TypedArray.set in Javascript. Implement all cases for TypedArray.set in Javascript, except the case where memmove is used. This makes it many times faster. R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/21353002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15985 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/runtime.cc b/src/runtime.cc index ed3527f..2cbaad1 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -907,6 +907,21 @@ TYPED_ARRAY_GETTER(Length, length) #undef TYPED_ARRAY_GETTER +// Return codes for Runtime_TypedArraySetFastCases. +// Should be synchronized with typedarray.js natives. +enum TypedArraySetResultCodes { + // Set from typed array of the same type. + // This is processed by TypedArraySetFastCases + TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0, + // Set from typed array of the different type, overlapping in memory. + TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1, + // Set from typed array of the different type, non-overlapping. + TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2, + // Set from non-typed array. + TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3 +}; + + RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { HandleScope scope(isolate); CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0); @@ -918,7 +933,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { "not_typed_array", HandleVector(NULL, 0))); if (!source_obj->IsJSTypedArray()) - return isolate->heap()->false_value(); + return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY); Handle target(JSTypedArray::cast(*target_obj)); Handle source(JSTypedArray::cast(*source_obj)); @@ -933,20 +948,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { return isolate->Throw(*isolate->factory()->NewRangeError( "typed_array_set_source_too_large", HandleVector(NULL, 0))); - Handle target_buffer(JSArrayBuffer::cast(target->buffer())); - Handle source_buffer(JSArrayBuffer::cast(source->buffer())); size_t target_offset = NumberToSize(isolate, target->byte_offset()); size_t source_offset = NumberToSize(isolate, source->byte_offset()); uint8_t* target_base = - static_cast(target_buffer->backing_store()) + target_offset; + static_cast( + JSArrayBuffer::cast(target->buffer())->backing_store()) + target_offset; uint8_t* source_base = - static_cast(source_buffer->backing_store()) + source_offset; + static_cast( + JSArrayBuffer::cast(source->buffer())->backing_store()) + source_offset; // Typed arrays of the same type: use memmove. if (target->type() == source->type()) { memmove(target_base + offset * target->element_size(), source_base, source_byte_length); - return isolate->heap()->true_value(); + return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE); } // Typed arrays of different types over the same backing store @@ -954,78 +969,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { source_base + source_byte_length > target_base) || (target_base <= source_base && target_base + target_byte_length > source_base)) { - size_t target_element_size = target->element_size(); - size_t source_element_size = source->element_size(); - - size_t source_length = NumberToSize(isolate, source->length()); - - // Copy left part - size_t left_index; - { - // First un-mutated byte after the next write - uint8_t* target_ptr = target_base + (offset + 1) * target_element_size; - // Next read at source_ptr. We do not care for memory changing before - // source_ptr - we have already copied it. - uint8_t* source_ptr = source_base; - for (left_index = 0; - left_index < source_length && target_ptr <= source_ptr; - left_index++) { - Handle v = Object::GetElement( - source, static_cast(left_index)); - JSObject::SetElement( - target, static_cast(offset + left_index), v, - NONE, kNonStrictMode); - target_ptr += target_element_size; - source_ptr += source_element_size; - } - } - // Copy right part - size_t right_index; - { - // First unmutated byte before the next write - uint8_t* target_ptr = - target_base + (offset + source_length - 1) * target_element_size; - // Next read before source_ptr. We do not care for memory changing after - // source_ptr - we have already copied it. - uint8_t* source_ptr = - source_base + source_length * source_element_size; - for (right_index = source_length - 1; - right_index >= left_index && target_ptr >= source_ptr; - right_index--) { - Handle v = Object::GetElement( - source, static_cast(right_index)); - JSObject::SetElement( - target, static_cast(offset + right_index), v, - NONE, kNonStrictMode); - target_ptr -= target_element_size; - source_ptr -= source_element_size; - } - } - // There can be at most 8 entries left in the middle that need buffering - // (because the largest element_size is 8 times the smallest). - ASSERT((right_index + 1) - left_index <= 8); - Handle temp[8]; - size_t idx; - for (idx = left_index; idx <= right_index; idx++) { - temp[idx - left_index] = Object::GetElement( - source, static_cast(idx)); - } - for (idx = left_index; idx <= right_index; idx++) { - JSObject::SetElement( - target, static_cast(offset + idx), temp[idx-left_index], - NONE, kNonStrictMode); - } + // We do not support overlapping ArrayBuffers + ASSERT( + JSArrayBuffer::cast(target->buffer())->backing_store() == + JSArrayBuffer::cast(source->buffer())->backing_store()); + return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING); } else { // Non-overlapping typed arrays - for (size_t idx = 0; idx < source_length; idx++) { - Handle value = Object::GetElement( - source, static_cast(idx)); - JSObject::SetElement( - target, static_cast(offset + idx), value, - NONE, kNonStrictMode); - } + return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING); } - - return isolate->heap()->true_value(); } diff --git a/src/typedarray.js b/src/typedarray.js index d5357b4..f9e732f 100644 --- a/src/typedarray.js +++ b/src/typedarray.js @@ -144,30 +144,103 @@ function CreateSubArray(elementSize, constructor) { } } +function TypedArraySetFromArrayLike(target, source, sourceLength, offset) { + if (offset > 0) { + for (var i = 0; i < sourceLength; i++) { + target[offset + i] = source[i]; + } + } + else { + for (var i = 0; i < sourceLength; i++) { + target[i] = source[i]; + } + } +} + +function TypedArraySetFromOverlappingTypedArray(target, source, offset) { + var sourceElementSize = source.BYTES_PER_ELEMENT; + var targetElementSize = target.BYTES_PER_ELEMENT; + var sourceLength = source.length; + + // Copy left part. + function CopyLeftPart() { + // First un-mutated byte after the next write + var targetPtr = target.byteOffset + (offset + 1) * targetElementSize; + // Next read at sourcePtr. We do not care for memory changing before + // sourcePtr - we have already copied it. + var sourcePtr = source.byteOffset; + for (var leftIndex = 0; + leftIndex < sourceLength && targetPtr <= sourcePtr; + leftIndex++) { + target[offset + leftIndex] = source[leftIndex]; + targetPtr += targetElementSize; + sourcePtr += sourceElementSize; + } + return leftIndex; + } + var leftIndex = CopyLeftPart(); + + // Copy rigth part; + function CopyRightPart() { + // First unmutated byte before the next write + var targetPtr = + target.byteOffset + (offset + sourceLength - 1) * targetElementSize; + // Next read before sourcePtr. We do not care for memory changing after + // sourcePtr - we have already copied it. + var sourcePtr = + source.byteOffset + sourceLength * sourceElementSize; + for(var rightIndex = sourceLength - 1; + rightIndex >= leftIndex && targetPtr >= sourcePtr; + rightIndex--) { + target[offset + rightIndex] = source[rightIndex]; + targetPtr -= targetElementSize; + sourcePtr -= sourceElementSize; + } + return rightIndex; + } + var rightIndex = CopyRightPart(); + + var temp = new $Array(rightIndex + 1 - leftIndex); + for (var i = leftIndex; i <= rightIndex; i++) { + temp[i - leftIndex] = source[i]; + } + for (i = leftIndex; i <= rightIndex; i++) { + target[offset + i] = temp[i - leftIndex]; + } +} + function TypedArraySet(obj, offset) { var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset); if (intOffset < 0) { throw MakeTypeError("typed_array_set_negative_offset"); } - if (%TypedArraySetFastCases(this, obj, intOffset)) - return; - - var l = obj.length; - if (IS_UNDEFINED(l)) { - if (IS_NUMBER(obj)) { - // For number as a first argument, throw TypeError - // instead of silently ignoring the call, so that - // the user knows (s)he did something wrong. - // (Consistent with Firefox and Blink/WebKit) - throw MakeTypeError("invalid_argument"); - } - return; - } - if (intOffset + l > this.length) { - throw MakeRangeError("typed_array_set_source_too_large"); - } - for (var i = 0; i < l; i++) { - this[intOffset + i] = obj[i]; + switch (%TypedArraySetFastCases(this, obj, intOffset)) { + // These numbers should be synchronized with runtime.cc. + case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE + return; + case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING + TypedArraySetFromOverlappingTypedArray(this, obj, intOffset); + return; + case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING + TypedArraySetFromArrayLike(this, obj, obj.length, intOffset); + return; + case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY + var l = obj.length; + if (IS_UNDEFINED(l)) { + if (IS_NUMBER(obj)) { + // For number as a first argument, throw TypeError + // instead of silently ignoring the call, so that + // the user knows (s)he did something wrong. + // (Consistent with Firefox and Blink/WebKit) + throw MakeTypeError("invalid_argument"); + } + return; + } + if (intOffset + l > this.length) { + throw MakeRangeError("typed_array_set_source_too_large"); + } + TypedArraySetFromArrayLike(this, obj, l, intOffset); + return; } }