Commit 50304f87 authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[csa] Add "Iterate" helper to IteratorBuiltinsAssembler

"Iterate" takes an iterable and a function of TNode<Object>, and
implements the iterator protocol to iterate the iterable, applying the
function to each element.

It handles exceptions thrown during iteration and closes the iterator as
appropriate -- the hope is that if the body of the iteration has no
exception-throwing nodes, TurboFan can dead-code eliminate this close.

In the future, we may want to add an array fast-path to this method;
centralising the implementation means that this fast-path will then be
used by all callers of Iterate.

Change-Id: I9fe2f862b78619fe21ea7cb6469ed7ba93f14a30
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3581770Reviewed-by: 's avatarFrank Tang <ftang@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80075}
parent 056f7e52
...@@ -3,13 +3,14 @@ ...@@ -3,13 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/builtins/builtins-iterator-gen.h" #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-collections-gen.h"
#include "src/builtins/builtins-string-gen.h" #include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h" #include "src/builtins/builtins.h"
#include "src/builtins/growable-fixed-array-gen.h"
#include "src/codegen/code-stub-assembler.h" #include "src/codegen/code-stub-assembler.h"
#include "src/compiler/code-assembler.h"
#include "src/heap/factory-inl.h" #include "src/heap/factory-inl.h"
namespace v8 { namespace v8 {
...@@ -131,6 +132,55 @@ TNode<Object> IteratorBuiltinsAssembler::IteratorValue( ...@@ -131,6 +132,55 @@ TNode<Object> IteratorBuiltinsAssembler::IteratorValue(
return var_value.value(); return var_value.value();
} }
void IteratorBuiltinsAssembler::Iterate(
TNode<Context> context, TNode<Object> iterable,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*> merged_variables) {
Iterate(context, iterable, GetIteratorMethod(context, iterable), func,
merged_variables);
}
void IteratorBuiltinsAssembler::Iterate(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterable_fn,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*> merged_variables) {
Label done(this);
IteratorRecord iterator_record = GetIterator(context, iterable, iterable_fn);
Label if_exception(this, Label::kDeferred);
TVARIABLE(Object, var_exception);
Label loop_start(this, merged_variables);
Goto(&loop_start);
BIND(&loop_start);
{
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done);
TNode<Object> next_value = IteratorValue(context, next);
{
compiler::ScopedExceptionHandler handler(this, &if_exception,
&var_exception);
func(next_value);
}
Goto(&loop_start);
}
BIND(&if_exception);
{
TNode<HeapObject> message = GetPendingMessage();
SetPendingMessage(TheHoleConstant());
IteratorCloseOnException(context, iterator_record);
CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
message);
Unreachable();
}
BIND(&done);
}
TNode<JSArray> IteratorBuiltinsAssembler::IterableToList( TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) { TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
GrowableFixedArray values(state()); GrowableFixedArray values(state());
...@@ -149,33 +199,26 @@ TNode<FixedArray> IteratorBuiltinsAssembler::IterableToFixedArray( ...@@ -149,33 +199,26 @@ TNode<FixedArray> IteratorBuiltinsAssembler::IterableToFixedArray(
void IteratorBuiltinsAssembler::FillFixedArrayFromIterable( void IteratorBuiltinsAssembler::FillFixedArrayFromIterable(
TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn, TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn,
GrowableFixedArray* values) { GrowableFixedArray* values) {
// 1. Let iteratorRecord be ? GetIterator(items, method). // 1. Let iteratorRecord be ? GetIterator(items, method) (handled by Iterate).
IteratorRecord iterator_record = GetIterator(context, iterable, iterator_fn);
// 2. Let values be a new empty List. // 2. Let values be a new empty List.
// The GrowableFixedArray has already been created. It's ok if we do this step // The GrowableFixedArray has already been created. It's ok if we do this step
// out of order, since creating an empty List is not observable. // out of order, since creating an empty List is not observable.
Label loop_start(this, {values->var_array(), values->var_length(), // 3. Let next be true. (handled by Iterate)
values->var_capacity()}), // 4. Repeat, while next is not false (handled by Iterate)
done(this); Iterate(context, iterable, iterator_fn,
Goto(&loop_start); [&values](TNode<Object> value) {
// 3. Let next be true. // Handled by Iterate:
// 4. Repeat, while next is not false // a. Set next to ? IteratorStep(iteratorRecord).
BIND(&loop_start); // b. If next is not false, then
{ // i. Let nextValue be ? IteratorValue(next).
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done); // ii. Append nextValue to the end of the List values.
// b. If next is not false, then values->Push(value);
// i. Let nextValue be ? IteratorValue(next). },
TNode<Object> next_value = IteratorValue(context, next); {values->var_array(), values->var_capacity(), values->var_length()});
// ii. Append nextValue to the end of the List values.
values->Push(next_value);
Goto(&loop_start);
}
BIND(&done);
} }
TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) { TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) {
...@@ -226,56 +269,48 @@ TNode<FixedArray> IteratorBuiltinsAssembler::StringListFromIterable( ...@@ -226,56 +269,48 @@ TNode<FixedArray> IteratorBuiltinsAssembler::StringListFromIterable(
// a. Return a new empty List. // a. Return a new empty List.
GotoIf(IsUndefined(iterable), &done); GotoIf(IsUndefined(iterable), &done);
// 2. Let iteratorRecord be ? GetIterator(items). // 2. Let iteratorRecord be ? GetIterator(items) (handled by Iterate).
IteratorRecord iterator_record = GetIterator(context, iterable);
// 3. Let list be a new empty List. // 3. Let list be a new empty List.
Label loop_start(this, // 4. Let next be true (handled by Iterate).
{list.var_array(), list.var_length(), list.var_capacity()}); // 5. Repeat, while next is not false (handled by Iterate).
Goto(&loop_start); Iterate(
// 4. Let next be true. context, iterable,
// 5. Repeat, while next is not false [&](TNode<Object> next_value) {
Label if_isnotstringtype(this, Label::kDeferred), // Handled by Iterate:
if_exception(this, Label::kDeferred); // a. Set next to ? IteratorStep(iteratorRecord).
BIND(&loop_start); // b. If next is not false, then
{ // i. Let nextValue be ? IteratorValue(next).
// a. Set next to ? IteratorStep(iteratorRecord).
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done); // ii. If Type(nextValue) is not String, then
// b. If next is not false, then Label if_isnotstringtype(this, Label::kDeferred), loop_body_end(this);
// i. Let nextValue be ? IteratorValue(next). GotoIf(TaggedIsSmi(next_value), &if_isnotstringtype);
TNode<Object> next_value = IteratorValue(context, next); TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
// ii. If Type(nextValue) is not String, then GotoIfNot(IsStringInstanceType(next_value_type), &if_isnotstringtype);
GotoIf(TaggedIsSmi(next_value), &if_isnotstringtype);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value)); // iii. Append nextValue to the end of the List list.
GotoIfNot(IsStringInstanceType(next_value_type), &if_isnotstringtype); list.Push(next_value);
// iii. Append nextValue to the end of the List list.
list.Push(next_value); Goto(&loop_body_end);
Goto(&loop_start);
// 5.b.ii // 5.b.ii
BIND(&if_isnotstringtype); BIND(&if_isnotstringtype);
{ {
// 1. Let error be ThrowCompletion(a newly created TypeError object). // 1. Let error be ThrowCompletion(a newly created TypeError object).
TVARIABLE(Object, var_exception);
{ CallRuntime(Runtime::kThrowTypeError, context,
compiler::ScopedExceptionHandler handler(this, &if_exception, SmiConstant(MessageTemplate::kIterableYieldedNonString),
&var_exception); next_value);
CallRuntime(Runtime::kThrowTypeError, context, // 2. Return ? IteratorClose(iteratorRecord, error). (handled by
SmiConstant(MessageTemplate::kIterableYieldedNonString), // Iterate).
next_value); Unreachable();
} }
Unreachable();
BIND(&loop_body_end);
// 2. Return ? IteratorClose(iteratorRecord, error). },
BIND(&if_exception); {list.var_array(), list.var_length(), list.var_capacity()});
TNode<HeapObject> message = GetPendingMessage(); Goto(&done);
SetPendingMessage(TheHoleConstant());
IteratorCloseOnException(context, iterator_record);
CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
message);
Unreachable();
}
}
BIND(&done); BIND(&done);
// 6. Return list. // 6. Return list.
......
...@@ -50,6 +50,16 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler { ...@@ -50,6 +50,16 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler {
TNode<Context> context, TNode<JSReceiver> result, TNode<Context> context, TNode<JSReceiver> result,
base::Optional<TNode<Map>> fast_iterator_result_map = base::nullopt); base::Optional<TNode<Map>> fast_iterator_result_map = base::nullopt);
void Iterate(TNode<Context> context, TNode<Object> iterable,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*>
merged_variables = {});
void Iterate(TNode<Context> context, TNode<Object> iterable,
TNode<Object> iterable_fn,
std::function<void(TNode<Object>)> func,
std::initializer_list<compiler::CodeAssemblerVariable*>
merged_variables = {});
// #sec-iterabletolist // #sec-iterabletolist
// Build a JSArray by iterating over {iterable} using {iterator_fn}, // Build a JSArray by iterating over {iterable} using {iterator_fn},
// following the ECMAscript operation with the same name. // following the ECMAscript operation with the same name.
......
...@@ -33,55 +33,52 @@ TemporalBuiltinsAssembler::TemporalInstantFixedArrayFromIterable( ...@@ -33,55 +33,52 @@ TemporalBuiltinsAssembler::TemporalInstantFixedArrayFromIterable(
// a. Return a new empty List. // a. Return a new empty List.
GotoIf(IsUndefined(iterable), &done); GotoIf(IsUndefined(iterable), &done);
// 2. Let iteratorRecord be ? GetIterator(items). // 2. Let iteratorRecord be ? GetIterator(items) (handled by Iterate).
IteratorRecord iterator_record = GetIterator(context, iterable);
// 3. Let list be a new empty List. // 3. Let list be a new empty List.
Label loop_start(this, // 3. Let next be true. (handled by Iterate).
{list.var_array(), list.var_length(), list.var_capacity()}); // 4. Repeat, while next is not false (handled by Iterate).
Goto(&loop_start); Iterate(context, iterable,
// 4. Let next be true. [&](TNode<Object> next_value) {
// 5. Repeat, while next is not false // Handled by Iterate:
Label if_isnottemporalinstant(this, Label::kDeferred), // a. Set next to ? IteratorStep(iteratorRecord).
if_exception(this, Label::kDeferred); // b. If next is not false, then
BIND(&loop_start); // i. Let nextValue be ? IteratorValue(next).
{
// a. Set next to ? IteratorStep(iteratorRecord). // ii. If Type(nextValue) is not Object or nextValue does not have
TNode<JSReceiver> next = IteratorStep(context, iterator_record, &done); // an [[InitializedTemporalInstant]] internal slot
// b. If next is not false, then Label if_isnottemporalinstant(this, Label::kDeferred),
// i. Let nextValue be ? IteratorValue(next). loop_body_end(this);
TNode<Object> next_value = IteratorValue(context, next); GotoIf(TaggedIsSmi(next_value), &if_isnottemporalinstant);
// ii. If Type(nextValue) is not Object or nextValue does not have an TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
// [[InitializedTemporalInstant]] internal slot GotoIfNot(IsTemporalInstantInstanceType(next_value_type),
GotoIf(TaggedIsSmi(next_value), &if_isnottemporalinstant); &if_isnottemporalinstant);
TNode<Uint16T> next_value_type = LoadInstanceType(CAST(next_value));
GotoIfNot(IsTemporalInstantInstanceType(next_value_type), // iii. Append nextValue to the end of the List list.
&if_isnottemporalinstant); list.Push(next_value);
// iii. Append nextValue to the end of the List list. Goto(&loop_body_end);
list.Push(next_value);
Goto(&loop_start); // 5.b.ii
// 5.b.ii BIND(&if_isnottemporalinstant);
BIND(&if_isnottemporalinstant); {
{ // 1. Let error be ThrowCompletion(a newly created TypeError
// 1. Let error be ThrowCompletion(a newly created TypeError object). // object).
TVARIABLE(Object, var_exception); CallRuntime(
{ Runtime::kThrowTypeError, context,
compiler::ScopedExceptionHandler handler(this, &if_exception, SmiConstant(MessageTemplate::kIterableYieldedNonString),
&var_exception); next_value);
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kIterableYieldedNonString), // 2. Return ? IteratorClose(iteratorRecord, error). (handled by
next_value); // Iterate).
} Unreachable();
Unreachable(); }
// 2. Return ? IteratorClose(iteratorRecord, error). BIND(&loop_body_end);
BIND(&if_exception); },
IteratorCloseOnException(context, iterator_record); {list.var_array(), list.var_length(), list.var_capacity()});
CallRuntime(Runtime::kReThrow, context, var_exception.value());
Unreachable(); Goto(&done);
}
}
BIND(&done); BIND(&done);
return list.ToFixedArray(); return list.ToFixedArray();
......
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