Commit 1e03479c authored by mvstanton's avatar mvstanton Committed by Commit bot

[builtins] Array.prototype.filter implemented as a TurboFan code stub.

BUG=

Review-Url: https://codereview.chromium.org/2680153005
Cr-Commit-Position: refs/heads/master@{#43965}
parent ed93e7c2
......@@ -3678,6 +3678,7 @@ void Genesis::InitializeGlobal_enable_fast_array_builtins() {
factory->NewStringFromAsciiChecked("prototype"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> array_prototype = Object::GetProperty(&it2).ToHandleChecked();
LookupIterator it3(array_prototype,
factory->NewStringFromAsciiChecked("forEach"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
......@@ -3708,6 +3709,19 @@ void Genesis::InitializeGlobal_enable_fast_array_builtins() {
Handle<JSFunction>::cast(some_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArraySome));
if (FLAG_experimental_array_builtins) {
LookupIterator it6(array_prototype,
factory->NewStringFromAsciiChecked("filter"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> filter_function =
Object::GetProperty(&it6).ToHandleChecked();
Handle<JSFunction>::cast(filter_function)
->set_code(isolate->builtins()->builtin(Builtins::kArrayFilter));
Handle<JSFunction>::cast(filter_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArrayFilter));
}
}
void Genesis::InitializeGlobal_harmony_sharedarraybuffer() {
......
......@@ -12,10 +12,16 @@ namespace internal {
class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
public:
explicit ArrayBuiltinCodeStubAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
: CodeStubAssembler(state), to_(this, MachineRepresentation::kTagged) {
to_.Bind(SmiConstant(0));
}
typedef std::function<Node*(Node* o, Node* len)> BuiltinResultGenerator;
typedef std::function<void(Node* a, Node* pK, Node* value)>
typedef std::function<Node*(Node* context, Node* o, Node* len)>
BuiltinResultGenerator;
typedef std::function<void(Node* context, Node* a)>
BuiltinResultIndexInitializer;
typedef std::function<void(Node* context, Node* value, Node* a,
Node* callback_result)>
CallResultProcessor;
void GenerateIteratingArrayBuiltinBody(
......@@ -87,7 +93,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Bind(&done);
Node* a = generator(o, len);
Node* a = generator(context, o, len);
// 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
// [Already done by the arguments adapter]
......@@ -109,6 +115,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
}
void GenerateIteratingArrayBuiltinLoopContinuation(
const BuiltinResultIndexInitializer& index_initializer,
const CallResultProcessor& processor) {
// TODO(ishell): use constants from Descriptor once the JSFunction linkage
// arguments are reordered.
......@@ -127,16 +134,19 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Node* context =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kContext);
index_initializer(context, a);
// 8. Repeat, while k < len
Variable k(this, MachineRepresentation::kTagged, initial_k);
Label loop(this, &k);
VariableList list({&k, &to_}, zone());
Label loop(this, list);
Label after_loop(this);
Goto(&loop);
Bind(&loop);
{
GotoUnlessNumberLessThan(k.value(), len, &after_loop);
Label done_element(this);
Label done_element(this, &to_);
// a. Let Pk be ToString(k).
Node* p_k = ToString(context, k.value());
......@@ -153,10 +163,11 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
// iv. ReturnIfAbrupt(funcResult).
Node* result = CallJS(CodeFactory::Call(isolate()), context, callbackfn,
this_arg, k_value, k.value(), o);
Node* callback_result =
CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
k_value, k.value(), o);
processor(a, p_k, result);
processor(context, k_value, a, callback_result);
Goto(&done_element);
Bind(&done_element);
......@@ -168,24 +179,67 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Return(a);
}
void ForEachProcessor(Node* a, Node* p_k, Node* value) {}
Node* FilterResultGenerator(Node* context, Node* o, Node* len) {
// 7. Let A be ArraySpeciesCreate(O, 0).
return ArraySpeciesCreate(context, o, SmiConstant(0));
}
void FilterResultIndexReinitializer(Node* context, Node* a) {
Variable merged_length(this, MachineRepresentation::kTagged);
Label has_length(this, &merged_length), not_js_array(this);
GotoIf(DoesntHaveInstanceType(a, JS_ARRAY_TYPE), &not_js_array);
merged_length.Bind(LoadJSArrayLength(a));
Goto(&has_length);
Bind(&not_js_array);
Node* len_property =
GetProperty(context, a, isolate()->factory()->length_string());
merged_length.Bind(
CallStub(CodeFactory::ToLength(isolate()), context, len_property));
Goto(&has_length);
Bind(&has_length);
Node* len = merged_length.value();
to_.Bind(len);
}
void ForEachProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {}
void SomeProcessor(Node* a, Node* p_k, Node* value) {
void SomeProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label false_continue(this), return_true(this);
BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
BranchIfToBooleanIsTrue(callback_result, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
}
void EveryProcessor(Node* a, Node* p_k, Node* value) {
void EveryProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label true_continue(this), return_false(this);
BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
BranchIfToBooleanIsTrue(callback_result, &true_continue, &return_false);
Bind(&return_false);
Return(FalseConstant());
Bind(&true_continue);
}
void FilterProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label true_continue(this, &to_), false_continue(this);
BranchIfToBooleanIsTrue(callback_result, &true_continue, &false_continue);
Bind(&true_continue);
// 1. let status be CreateDataPropertyOrThrow(A, ToString(to), kValue).
// 2. ReturnIfAbrupt(status)
Node* const p_to = ToString(context, to_.value());
CallRuntime(Runtime::kCreateDataProperty, context, a, p_to, value);
// 3. Increase to by 1.
to_.Bind(NumberInc(to_.value()));
Goto(&false_continue);
Bind(&false_continue);
}
private:
Node* VisitAllFastElementsOneKind(Node* context, ElementsKind kind,
Node* this_arg, Node* o, Node* len,
......@@ -196,7 +250,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Comment("begin VisitAllFastElementsOneKind");
Variable original_map(this, MachineRepresentation::kTagged);
original_map.Bind(LoadMap(o));
VariableList list({&original_map}, zone());
VariableList list({&original_map, &to_}, zone());
Node* last_index = nullptr;
BuildFastLoop(
list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode),
......@@ -235,9 +289,10 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
LoadDoubleWithHoleCheck(elements, offset, &hole_element);
value = AllocateHeapNumberWithValue(double_value);
}
Node* result = CallJS(CodeFactory::Call(isolate()), context,
callbackfn, this_arg, value, tagged_index, o);
processor(a, tagged_index, result);
Node* callback_result =
CallJS(CodeFactory::Call(isolate()), context, callbackfn,
this_arg, value, tagged_index, o);
processor(context, value, a, callback_result);
Goto(&one_element_done);
Bind(&hole_element);
......@@ -310,6 +365,8 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Goto(slow);
}
}
Variable to_;
};
TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
......@@ -470,47 +527,76 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
ForEachProcessor(a, p_k, value);
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
ForEachProcessor(context, value, a, callback_result);
});
}
TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.filter",
[=](Node* context, Node* o, Node* len) {
return FilterResultGenerator(context, o, len);
},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
FilterProcessor(context, value, a, callback_result);
},
CodeFactory::ArrayFilterLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* context, Node* a) {
FilterResultIndexReinitializer(context, a);
},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
FilterProcessor(context, value, a, callback_result);
});
}
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.forEach",
[=](Node*, Node*) { return UndefinedConstant(); },
[this](Node* a, Node* p_k, Node* value) {
ForEachProcessor(a, p_k, value);
[=](Node*, Node*, Node*) { return UndefinedConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
ForEachProcessor(context, value, a, callback_result);
},
CodeFactory::ArrayForEachLoopContinuation(isolate()));
}
TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
SomeProcessor(a, p_k, value);
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
SomeProcessor(context, value, a, callback_result);
});
}
TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.some", [=](Node*, Node*) { return FalseConstant(); },
[this](Node* a, Node* p_k, Node* value) { SomeProcessor(a, p_k, value); },
"Array.prototype.some",
[=](Node*, Node*, Node*) { return FalseConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
SomeProcessor(context, value, a, callback_result);
},
CodeFactory::ArraySomeLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* a, Node* p_k, Node* value) {
EveryProcessor(a, p_k, value);
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
EveryProcessor(context, value, a, callback_result);
});
}
TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.every", [=](Node*, Node*) { return TrueConstant(); },
[this](Node* a, Node* p_k, Node* value) {
EveryProcessor(a, p_k, value);
"Array.prototype.every",
[=](Node*, Node*, Node*) { return TrueConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
EveryProcessor(context, value, a, callback_result);
},
CodeFactory::ArrayEveryLoopContinuation(isolate()));
}
......
......@@ -293,6 +293,9 @@ class Isolate;
/* ES6 #sec-array.prototype.some */ \
TFJ(ArraySomeLoopContinuation, 6) \
TFJ(ArraySome, 2, kCallbackFn, kThisArg) \
/* ES6 #sec-array.prototype.filter */ \
TFJ(ArrayFilterLoopContinuation, 6) \
TFJ(ArrayFilter, 2, kCallbackFn, kThisArg) \
/* ES6 #sec-array.prototype.entries */ \
TFJ(ArrayPrototypeEntries, 0) \
/* ES6 #sec-array.prototype.keys */ \
......
......@@ -488,6 +488,12 @@ Callable CodeFactory::ArrayPush(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayPush(), BuiltinDescriptor(isolate));
}
// static
Callable CodeFactory::ArrayFilterLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayFilterLoopContinuation(),
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::ArrayForEachLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayForEachLoopContinuation(),
......
......@@ -181,6 +181,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayConstructor(Isolate* isolate);
static Callable ArrayPush(Isolate* isolate);
static Callable ArrayFilterLoopContinuation(Isolate* isolate);
static Callable ArrayForEachLoopContinuation(Isolate* isolate);
static Callable ArraySomeLoopContinuation(Isolate* isolate);
static Callable ArrayEveryLoopContinuation(Isolate* isolate);
......
......@@ -8080,6 +8080,16 @@ Node* CodeStubAssembler::AllocateJSArrayIterator(Node* array, Node* array_map,
return iterator;
}
Node* CodeStubAssembler::ArraySpeciesCreate(Node* context, Node* originalArray,
Node* len) {
// TODO(mvstanton): Install a fast path as well, which avoids the runtime
// call.
Node* constructor =
CallRuntime(Runtime::kArraySpeciesConstructor, context, originalArray);
return ConstructJS(CodeFactory::Construct(isolate()), context, constructor,
len);
}
Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
CSA_ASSERT(this, HasInstanceType(buffer, JS_ARRAY_BUFFER_TYPE));
......
......@@ -586,6 +586,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* AllocateJSArrayIterator(Node* array, Node* array_map, Node* map);
// Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
Node* ArraySpeciesCreate(Node* context, Node* originalArray, Node* len);
void FillFixedArrayWithValue(ElementsKind kind, Node* array, Node* from_index,
Node* to_index,
Heap::RootListIndex value_root_index,
......
......@@ -770,6 +770,8 @@ DEFINE_BOOL(builtins_in_stack_traces, false,
// builtins.cc
DEFINE_BOOL(enable_fast_array_builtins, false, "use optimized builtins")
DEFINE_BOOL(experimental_array_builtins, false,
"Experimental versions of array builtins")
DEFINE_BOOL(allow_unsafe_function_constructor, false,
"allow invoking the function constructor without security checks")
......
// Copyright 2017 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.
new BenchmarkSuite('Filter', [1000], [
new Benchmark('SmiFilter', false, false, 0,
Filter, SmiFilterSetup, ()=>{}),
new Benchmark('DoubleFilter', false, false, 0,
Filter, DoubleFilterSetup, ()=>{}),
new Benchmark('FastFilter', false, false, 0,
Filter, FastFilterSetup, ()=>{}),
new Benchmark('HoleySmiFilter', false, false, 0,
Filter, HoleySmiFilterSetup, ()=>{}),
new Benchmark('HoleyDoubleFilter', false, false, 0,
Filter, HoleyDoubleFilterSetup, ()=>{}),
new Benchmark('HoleyFastFilter', false, false, 0,
Filter, HoleyFastFilterSetup, ()=>{}),
new Benchmark('ObjectFilter', false, false, 0,
GenericFilter, ObjectFilterSetup, ()=>{}),
]);
var array;
var func;
var this_arg;
var result;
var array_size = 100;
function Filter() {
result = array.filter(func, this_arg);
}
function GenericFilter() {
result = Array.prototype.filter.call(array, func, this_arg);
}
function SmiFilterSetup() {
array = new Array();
for (var i = 0; i < array_size; i++) array[i] = i;
func = (value, index, object) => { return value % 2 === 0; };
}
function HoleySmiFilterSetup() {
array = new Array(array_size);
for (var i = 0; i < array_size; i++) {
if (i % 2 === 0) array[i] = i;
}
func = (value, index, object) => { return value % 2 === 0; };
}
function DoubleFilterSetup() {
array = new Array();
for (var i = 0; i < array_size; i++) array[i] = (i + 0.5);
func = (value, index, object) => { return Math.floor(value) % 2 === 0; };
}
function HoleyDoubleFilterSetup() {
array = new Array(array_size);
for (var i = 0; i < array_size; i++) {
if (i != 3) {
array[i] = (i + 0.5);
}
}
func = (value, index, object) => { return Math.floor(value) % 2 === 0; };
}
function FastFilterSetup() {
array = new Array();
for (var i = 0; i < array_size; i++) array[i] = 'value ' + i;
func = (value, index, object) => { return index % 2 === 0; };
}
function HoleyFastFilterSetup() {
array = new Array(array_size);
for (var i = 0; i < array_size; i++) {
if (i % 2 != 0) {
array[i] = 'value ' + i;
}
}
func = (value, index, object) => { return index % 2 === 0; };
}
function ObjectFilterSetup() {
array = { length: array_size };
for (var i = 0; i < array_size; i++) {
array[i] = i;
}
func = (value, index, object) => { return index % 2 === 0; };
}
// Copyright 2014 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.
load('../base.js');
load('filter.js');
var success = true;
function PrintResult(name, result) {
print(name + '-Array(Score): ' + result);
}
function PrintStep(name) {
print('Completed ' + name + '-Array...');
}
function PrintError(name, error) {
PrintResult(name, error);
success = false;
}
BenchmarkSuite.config.doWarmup = undefined;
BenchmarkSuite.config.doDeterministic = undefined;
BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
NotifyError: PrintError,
NotifyStep: PrintStep });
......@@ -327,6 +327,18 @@
"test_flags": ["sort"]
}
]
},
{
"name": "Arrays",
"path": ["Arrays"],
"main": "run.js",
"resources": [
"filter.js"
],
"results_regexp": "^Arrays\\-%s\\(Scope\\): (.+)$",
"tests": [
{"name": "Filter"}
]
}
]
}
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