Commit 7bd9eb7e authored by Hai Dang's avatar Hai Dang Committed by Commit Bot

Add fast paths to Array.from.

This reuses the fast path from IterableToList for Array.from. The fast
paths are taken when .from is called with the receiver Array and the only
argument is the iterable (no mapping function or thisArg).

Bug: v8:7980
Change-Id: I975b0c5e3f838262d7b71ad4dec5111fb031d746
Reviewed-on: https://chromium-review.googlesource.com/c/1297322
Commit-Queue: Hai Dang <dhai@google.com>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56993}
parent 69546692
......@@ -1954,7 +1954,29 @@ TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
TNode<Object> items = args.GetOptionalArgumentValue(0);
TNode<Object> receiver = args.GetReceiver();
Label fast_iterate(this), normal_iterate(this);
// Use fast path if:
// * |items| is the only argument, and
// * the receiver is the Array function.
GotoIfNot(Word32Equal(argc, Int32Constant(1)), &normal_iterate);
TNode<Object> array_function = LoadContextElement(
LoadNativeContext(context), Context::ARRAY_FUNCTION_INDEX);
Branch(WordEqual(array_function, receiver), &fast_iterate, &normal_iterate);
BIND(&fast_iterate);
{
IteratorBuiltinsAssembler iterator_assembler(state());
TVARIABLE(Object, var_fast_result);
iterator_assembler.FastIterableToList(context, items, &var_fast_result,
&normal_iterate);
args.PopAndReturn(var_fast_result.value());
}
BIND(&normal_iterate);
TNode<Object> map_function = args.GetOptionalArgumentValue(1);
// If map_function is not undefined, then ensure it's callable else throw.
......@@ -1973,7 +1995,6 @@ TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
Label iterable(this), not_iterable(this), finished(this), if_exception(this);
TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
TNode<Object> items = args.GetOptionalArgumentValue(0);
// The spec doesn't require ToObject to be called directly on the iterable
// branch, but it's part of GetMethod that is in the spec.
TNode<JSReceiver> array_like = ToObject_Inline(context, items);
......@@ -2010,7 +2031,7 @@ TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
}
// Construct the output array with empty length.
array = ConstructArrayLike(context, args.GetReceiver());
array = ConstructArrayLike(context, receiver);
// Actually get the iterator and throw if the iterator method does not yield
// one.
......@@ -2094,7 +2115,7 @@ TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
// Construct an array using the receiver as constructor with the same length
// as the input array.
array = ConstructArrayLike(context, args.GetReceiver(), length.value());
array = ConstructArrayLike(context, receiver, length.value());
TVARIABLE(Number, index, SmiConstant(0));
......
......@@ -262,30 +262,18 @@ TF_BUILTIN(IterableToListMayPreserveHoles, IteratorBuiltinsAssembler) {
TailCallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn);
}
// This builtin loads the property Symbol.iterator as the iterator, and has fast
// paths for fast arrays, for primitive strings, for sets and set iterators, and
// for map iterators. These fast paths will only be taken if Symbol.iterator and
// the Iterator prototype are not modified in a way that changes the original
// iteration behavior.
// * In case of fast holey arrays, holes will be converted to undefined to
// reflect iteration semantics. Note that replacement by undefined is only
// correct when the NoElements protector is valid.
// * In case of map/set iterators, there is an additional requirement that the
// iterator is not partially consumed. To be spec-compliant, after spreading
// the iterator is set to be exhausted.
TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
Label slow_path(this), check_string(this), check_map(this), check_set(this);
GotoIfForceSlowPath(&slow_path);
void IteratorBuiltinsAssembler::FastIterableToList(
TNode<Context> context, TNode<Object> iterable,
TVariable<Object>* var_result, Label* slow) {
Label done(this), check_string(this), check_map(this), check_set(this);
GotoIfNot(IsFastJSArrayWithNoCustomIteration(iterable, context),
&check_string);
// Fast path for fast JSArray.
TailCallBuiltin(Builtins::kCloneFastJSArrayFillingHoles, context, iterable);
*var_result =
CallBuiltin(Builtins::kCloneFastJSArrayFillingHoles, context, iterable);
Goto(&done);
BIND(&check_string);
{
......@@ -295,7 +283,8 @@ TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
iterable, context, &string_fast_call, &check_map);
BIND(&string_fast_call);
TailCallBuiltin(Builtins::kStringToList, context, iterable);
*var_result = CallBuiltin(Builtins::kStringToList, context, iterable);
Goto(&done);
}
BIND(&check_map);
......@@ -305,19 +294,48 @@ TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
state(), iterable, context, &map_fast_call, &check_set);
BIND(&map_fast_call);
TailCallBuiltin(Builtins::kMapIteratorToList, context, iterable);
*var_result = CallBuiltin(Builtins::kMapIteratorToList, context, iterable);
Goto(&done);
}
BIND(&check_set);
{
Label set_fast_call(this);
BranchIfIterableWithOriginalValueSetIterator(state(), iterable, context,
&set_fast_call, &slow_path);
&set_fast_call, slow);
BIND(&set_fast_call);
TailCallBuiltin(Builtins::kSetOrSetIteratorToList, context, iterable);
*var_result =
CallBuiltin(Builtins::kSetOrSetIteratorToList, context, iterable);
Goto(&done);
}
BIND(&done);
}
// This builtin loads the property Symbol.iterator as the iterator, and has fast
// paths for fast arrays, for primitive strings, for sets and set iterators, and
// for map iterators. These fast paths will only be taken if Symbol.iterator and
// the Iterator prototype are not modified in a way that changes the original
// iteration behavior.
// * In case of fast holey arrays, holes will be converted to undefined to
// reflect iteration semantics. Note that replacement by undefined is only
// correct when the NoElements protector is valid.
// * In case of map/set iterators, there is an additional requirement that the
// iterator is not partially consumed. To be spec-compliant, after spreading
// the iterator is set to be exhausted.
TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
Label slow_path(this);
GotoIfForceSlowPath(&slow_path);
TVARIABLE(Object, var_result);
FastIterableToList(context, iterable, &var_result, &slow_path);
Return(var_result.value());
BIND(&slow_path);
{
TNode<Object> iterator_fn = GetIteratorMethod(context, iterable);
......
......@@ -59,6 +59,9 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
// following the ECMAscript operation with the same name.
TNode<JSArray> IterableToList(TNode<Context> context, TNode<Object> iterable,
TNode<Object> iterator_fn);
void FastIterableToList(TNode<Context> context, TNode<Object> iterable,
TVariable<Object>* var_result, Label* slow);
};
} // namespace internal
......
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