Commit 87e1b85d authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

Reland "Add fast paths to Array.from."

This is a reland of 7bd9eb7e. No changes
to that patch other than adding a test case. The bug that lead to the
revert has been fixed in 9bf8f72c.

Original change's description:
> 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: Georg Neis <neis@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#56993}

Bug: v8:7980
Change-Id: Id081837946c0989ec2b31ce991f48d09e0219b09
Reviewed-on: https://chromium-review.googlesource.com/c/1317586Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57240}
parent fbeaeb26
......@@ -1915,7 +1915,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.
......@@ -1934,7 +1956,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);
......@@ -1971,7 +1992,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.
......@@ -2055,7 +2076,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));
......
......@@ -265,30 +265,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);
{
......@@ -298,7 +286,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);
......@@ -308,19 +297,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);
......
......@@ -72,6 +72,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
......
......@@ -45,3 +45,10 @@ const n = 130000;
for (let i = 0; i < n; ++i) x.set(i, String(i));
let a = [...x.keys()];
}
// Array.from
{
let x = new Set();
for (let i = 0; i < n; ++i) x.add(i);
let a = Array.from(x);
}
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