Commit fb29a554 authored by Hai Dang's avatar Hai Dang Committed by Commit Bot

Add fast path for spreading keys/values of JSMap and JSSet.

This CL extends IterableToListWithSymbolLookup with fast paths
for spreading keys/values iterators of JSMap, and values iterator
of JSSet (which is also the iterator of Set.prototype.keys() and
Set.prototype[Symbol.iterator]()). The fast paths are only taken
if the target still has original iteration behavior.

For iterators it is also required that the iterator is not
partially consumed. After spreading, to be spec-compliant, the
iterator is exhausted. Tests are added.

Bug: v8:7980
Change-Id: Ida74e5ecbbc5ba5488d13a40f2c4bda14c781cbf
Reviewed-on: https://chromium-review.googlesource.com/c/1276632Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Hai Dang <dhai@google.com>
Cr-Commit-Position: refs/heads/master@{#56716}
parent 676460f9
This diff is collapsed.
// 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.
#ifndef V8_BUILTINS_BUILTINS_COLLECTIONS_GEN_H_
#define V8_BUILTINS_BUILTINS_COLLECTIONS_GEN_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
using compiler::Node;
template <class T>
using TNode = compiler::TNode<T>;
void BranchIfIterableWithOriginalKeyOrValueMapIterator(
compiler::CodeAssemblerState* state, TNode<Object> iterable,
TNode<Context> context, compiler::CodeAssemblerLabel* if_true,
compiler::CodeAssemblerLabel* if_false);
void BranchIfIterableWithOriginalValueSetIterator(
compiler::CodeAssemblerState* state, TNode<Object> iterable,
TNode<Context> context, compiler::CodeAssemblerLabel* if_true,
compiler::CodeAssemblerLabel* if_false);
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_COLLECTIONS_GEN_H_
......@@ -678,6 +678,7 @@ namespace internal {
TFJ(MapPrototypeValues, 0, kReceiver) \
/* ES #sec-%mapiteratorprototype%.next */ \
TFJ(MapIteratorPrototypeNext, 0, kReceiver) \
TFS(MapIteratorToList, kSource) \
\
/* Math */ \
/* ES6 #sec-math.abs */ \
......@@ -1012,6 +1013,7 @@ namespace internal {
TFJ(SetPrototypeValues, 0, kReceiver) \
/* ES #sec-%setiteratorprototype%.next */ \
TFJ(SetIteratorPrototypeNext, 0, kReceiver) \
TFS(SetOrSetIteratorToList, kSource) \
\
/* SharedArrayBuffer */ \
CPP(SharedArrayBufferPrototypeGetByteLength) \
......
......@@ -5,6 +5,7 @@
#include "src/builtins/builtins-iterator-gen.h"
#include "src/builtins/growable-fixed-array-gen.h"
#include "src/builtins/builtins-collections-gen.h"
#include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
......@@ -261,18 +262,22 @@ TF_BUILTIN(IterableToListMayPreserveHoles, IteratorBuiltinsAssembler) {
TailCallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn);
}
// This builtin loads the property Symbol.iterator as the iterator, and has a
// fast path for fast arrays and another one for strings. 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.
// 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.
// 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);
Label slow_path(this), check_string(this), check_map(this), check_set(this);
GotoIfForceSlowPath(&slow_path);
......@@ -287,12 +292,32 @@ TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
StringBuiltinsAssembler string_assembler(state());
GotoIfNot(string_assembler.IsStringPrimitiveWithNoCustomIteration(iterable,
context),
&slow_path);
&check_map);
// Fast path for strings.
TailCallBuiltin(Builtins::kStringToList, context, iterable);
}
BIND(&check_map);
{
Label map_fast_call(this);
BranchIfIterableWithOriginalKeyOrValueMapIterator(
state(), iterable, context, &map_fast_call, &check_set);
BIND(&map_fast_call);
TailCallBuiltin(Builtins::kMapIteratorToList, context, iterable);
}
BIND(&check_set);
{
Label set_fast_call(this);
BranchIfIterableWithOriginalValueSetIterator(state(), iterable, context,
&set_fast_call, &slow_path);
BIND(&set_fast_call);
TailCallBuiltin(Builtins::kSetOrSetIteratorToList, context, iterable);
}
BIND(&slow_path);
{
TNode<Object> iterator_fn = GetIteratorMethod(context, iterable);
......
// 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.
var map = new Map([[1,2], [2,3], [3,4]]);
var iterator = map.keys();
assertEquals([1,2,3], [...map.keys()]);
assertEquals([1,2,3], [...iterator]);
assertEquals([], [...iterator]);
iterator = map.values();
assertEquals([2,3,4], [...iterator]);
assertEquals([], [...iterator]);
iterator = map.keys();
iterator.next();
assertEquals([2,3], [...iterator]);
assertEquals([], [...iterator]);
iterator = map.values();
var iterator2 = map.values();
map.delete(1);
assertEquals([3,4], [...iterator]);
assertEquals([], [...iterator]);
map.set(1,5);
assertEquals([3,4,5], [...iterator2]);
assertEquals([], [...iterator2]);
iterator = map.keys();
map.set(4,6);
assertEquals([2,3,1,4], [...iterator]);
assertEquals([], [...iterator]);
// 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.
var set = new Set([1,2,3]);
var iterator = set.keys();
assertEquals([1,2,3], [...set.keys()]);
assertEquals([1,2,3], [...iterator]);
assertEquals([], [...iterator]);
iterator = set.values();
assertEquals([1,2,3], [...iterator]);
assertEquals([], [...iterator]);
iterator = set.keys();
iterator.next();
assertEquals([2,3], [...iterator]);
assertEquals([], [...iterator]);
iterator = set.values();
var iterator2 = set.values();
set.delete(1);
assertEquals([2,3], [...iterator]);
set.add(4);
assertEquals([2,3,4], [...iterator2]);
iterator = set.keys();
set.add(1);
assertEquals([2,3,4,1], [...iterator]);
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