Commit 972f5d4b authored by Choongwoo Han's avatar Choongwoo Han Committed by Commit Bot

[typedarray] Port TA.p.filter to CSA

- Remove JS implementation of TA.p.filter
- Reimplement TA.p.filter as CSA
- This CL makes TA.p.filter 3x faster in microbenchmark
- Fix a spec bug: throw if buffer is detached while executing callback

Bug: v8:5929
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I2e14b6001d354ca6659cf65fff4ead2942ddc9ff
Reviewed-on: https://chromium-review.googlesource.com/912989Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51288}
parent e53807fb
......@@ -3029,6 +3029,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeEvery, 1, false);
SimpleInstallFunction(prototype, "fill",
Builtins::kTypedArrayPrototypeFill, 1, false);
SimpleInstallFunction(prototype, "filter",
Builtins::kTypedArrayPrototypeFilter, 1, false);
SimpleInstallFunction(prototype, "find", Builtins::kTypedArrayPrototypeFind,
1, false);
SimpleInstallFunction(prototype, "findIndex",
......
......@@ -1090,6 +1090,9 @@ namespace internal {
CPP(TypedArrayPrototypeCopyWithin) \
/* ES6 #sec-%typedarray%.prototype.fill */ \
CPP(TypedArrayPrototypeFill) \
/* ES6 #sec-%typedarray%.prototype.filter */ \
TFJ(TypedArrayPrototypeFilter, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 %TypedArray%.prototype.find */ \
TFJ(TypedArrayPrototypeFind, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
......
......@@ -936,7 +936,7 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::CreateByLength(
TNode<Smi> new_length =
LoadObjectField<Smi>(new_typed_array, JSTypedArray::kLengthOffset);
GotoIfNot(SmiLessThan(new_length, len), &if_length_is_not_short);
ThrowTypeError(context, MessageTemplate::kNotTypedArray);
ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
BIND(&if_length_is_not_short);
return new_typed_array;
......@@ -1612,6 +1612,105 @@ TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
}
// ES %TypedArray%.prototype.filter
TF_BUILTIN(TypedArrayPrototypeFilter, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.filter";
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
Label if_callback_not_callable(this, Label::kDeferred),
detached(this, Label::kDeferred);
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
TNode<Object> receiver = args.GetReceiver();
TNode<JSTypedArray> source =
ValidateTypedArray(context, receiver, method_name);
// 3. Let len be O.[[ArrayLength]].
TNode<Smi> length = LoadObjectField<Smi>(source, JSTypedArray::kLengthOffset);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
TNode<Object> callbackfn = args.GetOptionalArgumentValue(0);
GotoIf(TaggedIsSmi(callbackfn), &if_callback_not_callable);
GotoIfNot(IsCallable(callbackfn), &if_callback_not_callable);
// 5. If thisArg is present, let T be thisArg; else let T be undefined.
TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
TNode<JSArrayBuffer> source_buffer =
LoadObjectField<JSArrayBuffer>(source, JSArrayBufferView::kBufferOffset);
TNode<Word32T> elements_kind = LoadElementsKind(source);
GrowableFixedArray values(state());
VariableList vars(
{values.var_array(), values.var_length(), values.var_capacity()}, zone());
// 6. Let kept be a new empty List.
// 7. Let k be 0.
// 8. Let captured be 0.
// 9. Repeat, while k < len
BuildFastLoop(
vars, SmiConstant(0), length,
[&](Node* index) {
GotoIf(IsDetachedBuffer(source_buffer), &detached);
TVARIABLE(Number, value);
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
DispatchTypedArrayByElementsKind(
elements_kind,
[&](ElementsKind kind, int size, int typed_array_fun_index) {
TNode<IntPtrT> backing_store =
UncheckedCast<IntPtrT>(LoadDataPtr(source));
value = CAST(LoadFixedTypedArrayElementAsTagged(
backing_store, index, kind, ParameterMode::SMI_PARAMETERS));
});
// c. Let selected be ToBoolean(Call(callbackfn, T, kValue, k, O))
Node* selected =
CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
value.value(), index, source);
Label true_continue(this), false_continue(this);
BranchIfToBooleanIsTrue(selected, &true_continue, &false_continue);
BIND(&true_continue);
// d. If selected is true, then
// i. Append kValue to the end of kept.
// ii. Increase captured by 1.
values.Push(value.value());
Goto(&false_continue);
BIND(&false_continue);
},
1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
TNode<JSArray> values_array = values.ToJSArray(context);
TNode<Smi> captured = LoadFastJSArrayLength(values_array);
// 10. Let A be ? TypedArraySpeciesCreate(O, captured).
TNode<JSTypedArray> result_array =
SpeciesCreateByLength(context, source, captured, method_name);
// 11. Let n be 0.
// 12. For each element e of kept, do
// a. Perform ! Set(A, ! ToString(n), e, true).
// b. Increment n by 1.
CallRuntime(Runtime::kTypedArrayCopyElements, context, result_array,
values_array, captured);
// 13. Return A.
args.PopAndReturn(result_array);
BIND(&if_callback_not_callable);
ThrowTypeError(context, MessageTemplate::kCalledNonCallable, callbackfn);
BIND(&detached);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
}
#undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
} // namespace internal
......
......@@ -19,10 +19,7 @@ var InnerArrayJoin;
var InnerArraySort;
var InnerArrayToLocaleString;
var InternalArray = utils.InternalArray;
var MathMax = global.Math.max;
var MathMin = global.Math.min;
var iteratorSymbol = utils.ImportNow("iterator_symbol");
var speciesSymbol = utils.ImportNow("species_symbol");
macro TYPED_ARRAYS(FUNCTION)
FUNCTION(Uint8Array, 1)
......@@ -56,25 +53,6 @@ utils.Import(function(from) {
InnerArrayToLocaleString = from.InnerArrayToLocaleString;
});
// ES2015 7.3.20
function SpeciesConstructor(object, defaultConstructor) {
var constructor = object.constructor;
if (IS_UNDEFINED(constructor)) {
return defaultConstructor;
}
if (!IS_RECEIVER(constructor)) {
throw %make_type_error(kConstructorNotReceiver);
}
var species = constructor[speciesSymbol];
if (IS_NULL_OR_UNDEFINED(species)) {
return defaultConstructor;
}
if (%IsConstructor(species)) {
return species;
}
throw %make_type_error(kSpeciesNotConstructor);
}
// --------------- Typed Arrays ---------------------
// ES6 section 22.2.3.5.1 ValidateTypedArray ( O )
......@@ -85,20 +63,6 @@ function ValidateTypedArray(array, methodName) {
throw %make_type_error(kDetachedOperation, methodName);
}
function TypedArrayDefaultConstructor(typedArray) {
switch (%_ClassOf(typedArray)) {
macro TYPED_ARRAY_CONSTRUCTOR_CASE(NAME, ELEMENT_SIZE)
case "NAME":
return GlobalNAME;
endmacro
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR_CASE)
}
// The TypeError should not be generated since all callers should
// have already called ValidateTypedArray.
throw %make_type_error(kIncompatibleMethodReceiver,
"TypedArrayDefaultConstructor", this);
}
function TypedArrayCreate(constructor, arg0, arg1, arg2) {
if (IS_UNDEFINED(arg1)) {
var newTypedArray = new constructor(arg0);
......@@ -112,50 +76,6 @@ function TypedArrayCreate(constructor, arg0, arg1, arg2) {
return newTypedArray;
}
function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2) {
var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
var constructor = SpeciesConstructor(exemplar, defaultConstructor);
return TypedArrayCreate(constructor, arg0, arg1, arg2);
}
// The following functions cannot be made efficient on sparse arrays while
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
function InnerTypedArrayFilter(f, receiver, array, length, result) {
var result_length = 0;
for (var i = 0; i < length; i++) {
if (i in array) {
var element = array[i];
if (%_Call(f, receiver, element, i, array)) {
%CreateDataProperty(result, result_length, element);
result_length++;
}
}
}
return result;
}
// ES6 draft 07-15-13, section 22.2.3.9
DEFINE_METHOD_LEN(
GlobalTypedArray.prototype,
filter(f, thisArg) {
ValidateTypedArray(this, "%TypeArray%.prototype.filter");
var length = %_TypedArrayGetLength(this);
if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
var result = new InternalArray();
InnerTypedArrayFilter(f, thisArg, this, length, result);
var captured = result.length;
var output = TypedArraySpeciesCreate(this, captured);
for (var i = 0; i < captured; i++) {
output[i] = result[i];
}
return output;
},
1 /* Set function length. */
);
// ES6 draft 05-18-15, section 22.2.3.25
DEFINE_METHOD(
GlobalTypedArray.prototype,
......
// Copyright 2018 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.
// Flags: --allow-natives-syntax
var typedArrayConstructors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Uint32Array,
Int32Array,
Uint8ClampedArray,
Float32Array,
Float64Array];
function TestTypedArrayFilter(constructor) {
assertEquals(1, constructor.prototype.filter.length);
// Throw type error if source array is detached while executing a callback
let ta1 = new constructor(10);
assertThrows(() =>
ta1.filter(() => %ArrayBufferNeuter(ta1.buffer))
, TypeError);
// A new typed array should be created after finishing callbacks
var speciesCreated = 0;
class MyTypedArray extends constructor {
static get [Symbol.species]() {
return function() {
speciesCreated++;
return new constructor(10);
};
}
}
new MyTypedArray(10).filter(() => {
assertEquals(0, speciesCreated);
return true;
});
assertEquals(1, speciesCreated);
// A new typed array should be initialized to 0s
class LongTypedArray extends constructor {
static get [Symbol.species]() {
return function(len) {
return new constructor(len * 2);
}
}
}
let ta2 = new LongTypedArray(3).fill(1);
let ta3 = ta2.filter((val, index, array) => val > 0);
assertArrayEquals(ta3, [1, 1, 1, 0, 0, 0]);
assertEquals(ta3.constructor, constructor);
// Throw if a new typed array is too small
class ShortTypedArray extends constructor {
static get [Symbol.species]() {
return function(len) {
return new constructor(len/2);
}
}
}
assertThrows(() => new ShortTypedArray(10).filter(() => true));
// Throw if callback is not callable
assertThrows(() => new constructor(10).filter(123));
assertThrows(() => new constructor(10).filter({}));
// Empty result
assertEquals(new constructor(10).filter(() => false), new constructor(0));
// If a new typed array shares a buffer with a source array
let ab = new ArrayBuffer(100);
class SharedBufferTypedArray extends constructor {
static get [Symbol.species]() {
return function(len) {
return new constructor(ab, 0, 5);
}
}
}
let ta4 = new SharedBufferTypedArray(ab, 0, 5).fill(1);
let ta5 = ta4.filter(() => {
ta4[0] = 123;
ta4[2] = 123;
return true;
});
assertEquals(ta4.buffer, ta5.buffer);
assertArrayEquals(ta4, [1, 1, 123, 1, 1]);
assertArrayEquals(ta5, [1, 1, 123, 1, 1]);
// If a new typed array has a different type with source array
for (let j = 0; j < typedArrayConstructors.length; j++) {
let otherConstructor = typedArrayConstructors[j];
class OtherTypedArray extends constructor {
static get [Symbol.species]() {
return function(len) {
return new otherConstructor(len);
}
}
}
let ta6 = new OtherTypedArray(10).fill(123);
assertEquals(ta6.filter(() => true), new otherConstructor(10).fill(123));
}
}
for (i = 0; i < typedArrayConstructors.length; i++) {
TestTypedArrayFilter(typedArrayConstructors[i]);
}
......@@ -99,7 +99,6 @@
'built-ins/TypedArrays/internals/DefineOwnProperty/tonumber-value-detached-buffer': [FAIL],
# Some TypedArray methods throw due to the same bug, from Get
'built-ins/TypedArray/prototype/every/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer': [FAIL],
'built-ins/TypedArray/prototype/find/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/findIndex/predicate-may-detach-buffer': [FAIL],
'built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer': [FAIL],
......
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