Commit 58b503c2 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Migrate collections to JSCallReducer

Bug: v8:7340, v8:7250
Change-Id: I57f78fa5ad261f041b66986918c427821a57a6e1
Reviewed-on: https://chromium-review.googlesource.com/995472Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52356}
parent bffeab32
......@@ -296,343 +296,6 @@ Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) {
return Replace(value);
}
Reduction JSBuiltinReducer::ReduceCollectionIterator(
Node* node, InstanceType collection_instance_type,
int collection_iterator_map_index) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (NodeProperties::HasInstanceTypeWitness(receiver, effect,
collection_instance_type)) {
// Figure out the proper collection iterator map.
Handle<Map> collection_iterator_map(
Map::cast(native_context()->get(collection_iterator_map_index)),
isolate());
// Load the OrderedHashTable from the {receiver}.
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
receiver, effect, control);
// Create the JSCollectionIterator result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSCollectionIterator::kSize, NOT_TENURED, Type::OtherObject());
a.Store(AccessBuilder::ForMap(), collection_iterator_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSCollectionIteratorTable(), table);
a.Store(AccessBuilder::ForJSCollectionIteratorIndex(),
jsgraph()->ZeroConstant());
Node* value = effect = a.Finish();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSBuiltinReducer::ReduceCollectionSize(
Node* node, InstanceType collection_instance_type) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (NodeProperties::HasInstanceTypeWitness(receiver, effect,
collection_instance_type)) {
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
receiver, effect, control);
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
table, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSBuiltinReducer::ReduceCollectionIteratorNext(
Node* node, int entry_size, Handle<HeapObject> empty_collection,
InstanceType collection_iterator_instance_type_first,
InstanceType collection_iterator_instance_type_last) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// A word of warning to begin with: This whole method might look a bit
// strange at times, but that's mostly because it was carefully handcrafted
// to allow for full escape analysis and scalar replacement of both the
// collection iterator object and the iterator results, including the
// key-value arrays in case of Set/Map entry iteration.
//
// TODO(turbofan): Currently the escape analysis (and the store-load
// forwarding) is unable to eliminate the allocations for the key-value
// arrays in case of Set/Map entry iteration, and we should investigate
// how to update the escape analysis / arrange the graph in a way that
// this becomes possible.
// Infer the {receiver} instance type.
InstanceType receiver_instance_type;
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
DCHECK_NE(0, receiver_maps.size());
receiver_instance_type = receiver_maps[0]->instance_type();
for (size_t i = 1; i < receiver_maps.size(); ++i) {
if (receiver_maps[i]->instance_type() != receiver_instance_type) {
return NoChange();
}
}
if (receiver_instance_type < collection_iterator_instance_type_first ||
receiver_instance_type > collection_iterator_instance_type_last) {
return NoChange();
}
// Transition the JSCollectionIterator {receiver} if necessary
// (i.e. there were certain mutations while we're iterating).
{
Node* done_loop;
Node* done_eloop;
Node* loop = control =
graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
// Check if reached the final table of the {receiver}.
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, effect, control);
Node* next_table = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNextTable()),
table, effect, control);
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table);
control =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// Abort the {loop} when we reach the final table.
done_loop = graph()->NewNode(common()->IfTrue(), control);
done_eloop = effect;
// Migrate to the {next_table} otherwise.
control = graph()->NewNode(common()->IfFalse(), control);
// Self-heal the {receiver}s index.
Node* index = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, effect, control);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex);
auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, Operator::kEliminatable);
index = effect =
graph()->NewNode(common()->Call(call_descriptor),
jsgraph()->HeapConstant(callable.code()), table, index,
jsgraph()->NoContextConstant(), effect);
NodeProperties::SetType(index, type_cache_.kFixedArrayLengthType);
// Update the {index} and {table} on the {receiver}.
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, index, effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, next_table, effect, control);
// Tie the knot.
loop->ReplaceInput(1, control);
eloop->ReplaceInput(1, effect);
control = done_loop;
effect = done_eloop;
}
// Get current index and table from the JSCollectionIterator {receiver}.
Node* index = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, effect, control);
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, effect, control);
// Create the {JSIteratorResult} first to ensure that we always have
// a dominating Allocate node for the allocation folding phase.
Node* iterator_result = effect = graph()->NewNode(
javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(),
jsgraph()->TrueConstant(), context, effect);
// Look for the next non-holey key, starting from {index} in the {table}.
Node* controls[2];
Node* effects[3];
{
// Compute the currently used capacity.
Node* number_of_buckets = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()),
table, effect, control);
Node* number_of_elements = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
table, effect, control);
Node* number_of_deleted_elements = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()),
table, effect, control);
Node* used_capacity =
graph()->NewNode(simplified()->NumberAdd(), number_of_elements,
number_of_deleted_elements);
// Skip holes and update the {index}.
Node* loop = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* iloop = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);
NodeProperties::SetType(iloop, type_cache_.kFixedArrayLengthType);
{
Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), iloop,
used_capacity);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop);
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = eloop;
{
// Mark the {receiver} as exhausted.
efalse0 = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSCollectionIteratorTable()),
receiver, jsgraph()->HeapConstant(empty_collection), efalse0,
if_false0);
controls[0] = if_false0;
effects[0] = efalse0;
}
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = eloop;
{
// Load the key of the entry.
Node* entry_start_position = graph()->NewNode(
simplified()->NumberAdd(),
graph()->NewNode(
simplified()->NumberAdd(),
graph()->NewNode(simplified()->NumberMultiply(), iloop,
jsgraph()->Constant(entry_size)),
number_of_buckets),
jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex));
Node* entry_key = etrue0 = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
table, entry_start_position, etrue0, if_true0);
// Advance the index.
Node* index = graph()->NewNode(simplified()->NumberAdd(), iloop,
jsgraph()->OneConstant());
Node* check1 =
graph()->NewNode(simplified()->ReferenceEqual(), entry_key,
jsgraph()->TheHoleConstant());
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check1, if_true0);
{
// Abort loop with resulting value.
Node* control = graph()->NewNode(common()->IfFalse(), branch1);
Node* effect = etrue0;
Node* value = effect =
graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
entry_key, effect, control);
Node* done = jsgraph()->FalseConstant();
// Advance the index on the {receiver}.
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, index, effect, control);
// The actual {value} depends on the {receiver} iteration type.
switch (receiver_instance_type) {
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
break;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
value = effect =
graph()->NewNode(javascript()->CreateKeyValueArray(), value,
value, context, effect);
break;
case JS_MAP_VALUE_ITERATOR_TYPE:
value = effect = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForFixedArrayElement()),
table,
graph()->NewNode(
simplified()->NumberAdd(), entry_start_position,
jsgraph()->Constant(OrderedHashMap::kValueOffset)),
effect, control);
break;
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
value = effect = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForFixedArrayElement()),
table,
graph()->NewNode(
simplified()->NumberAdd(), entry_start_position,
jsgraph()->Constant(OrderedHashMap::kValueOffset)),
effect, control);
value = effect =
graph()->NewNode(javascript()->CreateKeyValueArray(),
entry_key, value, context, effect);
break;
default:
UNREACHABLE();
break;
}
// Store final {value} and {done} into the {iterator_result}.
effect =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForJSIteratorResultValue()),
iterator_result, value, effect, control);
effect =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForJSIteratorResultDone()),
iterator_result, done, effect, control);
controls[1] = control;
effects[1] = effect;
}
// Continue with next loop index.
loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1));
eloop->ReplaceInput(1, etrue0);
iloop->ReplaceInput(1, index);
}
}
control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls);
effect = graph()->NewNode(common()->EffectPhi(2), 3, effects);
}
// Yield the final {iterator_result}.
ReplaceWithValue(node, iterator_result, effect, control);
return Replace(iterator_result);
}
// ES6 section 20.3.3.1 Date.now ( )
Reduction JSBuiltinReducer::ReduceDateNow(Node* node) {
NodeProperties::RemoveValueInputs(node);
......@@ -850,40 +513,12 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kGlobalIsNaN:
reduction = ReduceGlobalIsNaN(node);
break;
case kMapEntries:
return ReduceCollectionIterator(
node, JS_MAP_TYPE, Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX);
case kMapKeys:
return ReduceCollectionIterator(node, JS_MAP_TYPE,
Context::MAP_KEY_ITERATOR_MAP_INDEX);
case kMapSize:
return ReduceCollectionSize(node, JS_MAP_TYPE);
case kMapValues:
return ReduceCollectionIterator(node, JS_MAP_TYPE,
Context::MAP_VALUE_ITERATOR_MAP_INDEX);
case kMapIteratorNext:
return ReduceCollectionIteratorNext(
node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE);
break;
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;
case kObjectCreate:
reduction = ReduceObjectCreate(node);
break;
case kSetEntries:
return ReduceCollectionIterator(
node, JS_SET_TYPE, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX);
case kSetSize:
return ReduceCollectionSize(node, JS_SET_TYPE);
case kSetValues:
return ReduceCollectionIterator(node, JS_SET_TYPE,
Context::SET_VALUE_ITERATOR_MAP_INDEX);
case kSetIteratorNext:
return ReduceCollectionIteratorNext(
node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(),
FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE);
case kArrayBufferIsView:
return ReduceArrayBufferIsView(node);
case kDataViewByteLength:
......
......@@ -42,15 +42,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceTypedArrayToStringTag(Node* node);
Reduction ReduceArrayIsArray(Node* node);
Reduction ReduceCollectionIterator(Node* node,
InstanceType collection_instance_type,
int collection_iterator_map_index);
Reduction ReduceCollectionSize(Node* node,
InstanceType collection_instance_type);
Reduction ReduceCollectionIteratorNext(
Node* node, int entry_size, Handle<HeapObject> empty_collection,
InstanceType collection_iterator_instance_type_first,
InstanceType collection_iterator_instance_type_last);
Reduction ReduceDateNow(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceGlobalIsFinite(Node* node);
......
......@@ -3499,6 +3499,33 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReducePromisePrototypeFinally(node);
case Builtins::kPromisePrototypeThen:
return ReducePromisePrototypeThen(node);
case Builtins::kMapPrototypeEntries:
return ReduceCollectionIteration(node, CollectionKind::kMap,
IterationKind::kEntries);
case Builtins::kMapPrototypeKeys:
return ReduceCollectionIteration(node, CollectionKind::kMap,
IterationKind::kKeys);
case Builtins::kMapPrototypeGetSize:
return ReduceCollectionPrototypeSize(node, CollectionKind::kMap);
case Builtins::kMapPrototypeValues:
return ReduceCollectionIteration(node, CollectionKind::kMap,
IterationKind::kValues);
case Builtins::kMapIteratorPrototypeNext:
return ReduceCollectionIteratorPrototypeNext(
node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE);
case Builtins::kSetPrototypeEntries:
return ReduceCollectionIteration(node, CollectionKind::kSet,
IterationKind::kEntries);
case Builtins::kSetPrototypeGetSize:
return ReduceCollectionPrototypeSize(node, CollectionKind::kSet);
case Builtins::kSetPrototypeValues:
return ReduceCollectionIteration(node, CollectionKind::kSet,
IterationKind::kValues);
case Builtins::kSetIteratorPrototypeNext:
return ReduceCollectionIteratorPrototypeNext(
node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(),
FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE);
default:
break;
}
......@@ -6053,6 +6080,344 @@ Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) {
return Replace(value);
}
namespace {
InstanceType InstanceTypeForCollectionKind(CollectionKind kind) {
switch (kind) {
case CollectionKind::kMap:
return JS_MAP_TYPE;
case CollectionKind::kSet:
return JS_SET_TYPE;
}
UNREACHABLE();
}
} // namespace
Reduction JSCallReducer::ReduceCollectionIteration(
Node* node, CollectionKind collection_kind, IterationKind iteration_kind) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (NodeProperties::HasInstanceTypeWitness(
receiver, effect, InstanceTypeForCollectionKind(collection_kind))) {
Node* js_create_iterator = effect = graph()->NewNode(
javascript()->CreateCollectionIterator(collection_kind, iteration_kind),
receiver, context, effect, control);
ReplaceWithValue(node, js_create_iterator, effect);
return Replace(js_create_iterator);
}
return NoChange();
}
Reduction JSCallReducer::ReduceCollectionPrototypeSize(
Node* node, CollectionKind collection_kind) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (NodeProperties::HasInstanceTypeWitness(
receiver, effect, InstanceTypeForCollectionKind(collection_kind))) {
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
receiver, effect, control);
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
table, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext(
Node* node, int entry_size, Handle<HeapObject> empty_collection,
InstanceType collection_iterator_instance_type_first,
InstanceType collection_iterator_instance_type_last) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// A word of warning to begin with: This whole method might look a bit
// strange at times, but that's mostly because it was carefully handcrafted
// to allow for full escape analysis and scalar replacement of both the
// collection iterator object and the iterator results, including the
// key-value arrays in case of Set/Map entry iteration.
//
// TODO(turbofan): Currently the escape analysis (and the store-load
// forwarding) is unable to eliminate the allocations for the key-value
// arrays in case of Set/Map entry iteration, and we should investigate
// how to update the escape analysis / arrange the graph in a way that
// this becomes possible.
// Infer the {receiver} instance type.
InstanceType receiver_instance_type;
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
DCHECK_NE(0, receiver_maps.size());
receiver_instance_type = receiver_maps[0]->instance_type();
for (size_t i = 1; i < receiver_maps.size(); ++i) {
if (receiver_maps[i]->instance_type() != receiver_instance_type) {
return NoChange();
}
}
if (receiver_instance_type < collection_iterator_instance_type_first ||
receiver_instance_type > collection_iterator_instance_type_last) {
return NoChange();
}
// Transition the JSCollectionIterator {receiver} if necessary
// (i.e. there were certain mutations while we're iterating).
{
Node* done_loop;
Node* done_eloop;
Node* loop = control =
graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
// Check if reached the final table of the {receiver}.
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, effect, control);
Node* next_table = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNextTable()),
table, effect, control);
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table);
control =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// Abort the {loop} when we reach the final table.
done_loop = graph()->NewNode(common()->IfTrue(), control);
done_eloop = effect;
// Migrate to the {next_table} otherwise.
control = graph()->NewNode(common()->IfFalse(), control);
// Self-heal the {receiver}s index.
Node* index = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, effect, control);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex);
auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, Operator::kEliminatable);
index = effect =
graph()->NewNode(common()->Call(call_descriptor),
jsgraph()->HeapConstant(callable.code()), table, index,
jsgraph()->NoContextConstant(), effect);
index = effect = graph()->NewNode(
common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), index,
effect, control);
// Update the {index} and {table} on the {receiver}.
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, index, effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, next_table, effect, control);
// Tie the knot.
loop->ReplaceInput(1, control);
eloop->ReplaceInput(1, effect);
control = done_loop;
effect = done_eloop;
}
// Get current index and table from the JSCollectionIterator {receiver}.
Node* index = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, effect, control);
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
receiver, effect, control);
// Create the {JSIteratorResult} first to ensure that we always have
// a dominating Allocate node for the allocation folding phase.
Node* iterator_result = effect = graph()->NewNode(
javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(),
jsgraph()->TrueConstant(), context, effect);
// Look for the next non-holey key, starting from {index} in the {table}.
Node* controls[2];
Node* effects[3];
{
// Compute the currently used capacity.
Node* number_of_buckets = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()),
table, effect, control);
Node* number_of_elements = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
table, effect, control);
Node* number_of_deleted_elements = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()),
table, effect, control);
Node* used_capacity =
graph()->NewNode(simplified()->NumberAdd(), number_of_elements,
number_of_deleted_elements);
// Skip holes and update the {index}.
Node* loop = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* iloop = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);
Node* index = effect = graph()->NewNode(
common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), iloop,
eloop, control);
{
Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index,
used_capacity);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop);
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
{
// Mark the {receiver} as exhausted.
efalse0 = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSCollectionIteratorTable()),
receiver, jsgraph()->HeapConstant(empty_collection), efalse0,
if_false0);
controls[0] = if_false0;
effects[0] = efalse0;
}
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
{
// Load the key of the entry.
Node* entry_start_position = graph()->NewNode(
simplified()->NumberAdd(),
graph()->NewNode(
simplified()->NumberAdd(),
graph()->NewNode(simplified()->NumberMultiply(), index,
jsgraph()->Constant(entry_size)),
number_of_buckets),
jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex));
Node* entry_key = etrue0 = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
table, entry_start_position, etrue0, if_true0);
// Advance the index.
index = graph()->NewNode(simplified()->NumberAdd(), index,
jsgraph()->OneConstant());
Node* check1 =
graph()->NewNode(simplified()->ReferenceEqual(), entry_key,
jsgraph()->TheHoleConstant());
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check1, if_true0);
{
// Abort loop with resulting value.
Node* control = graph()->NewNode(common()->IfFalse(), branch1);
Node* effect = etrue0;
Node* value = effect =
graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
entry_key, effect, control);
Node* done = jsgraph()->FalseConstant();
// Advance the index on the {receiver}.
effect = graph()->NewNode(
simplified()->StoreField(
AccessBuilder::ForJSCollectionIteratorIndex()),
receiver, index, effect, control);
// The actual {value} depends on the {receiver} iteration type.
switch (receiver_instance_type) {
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
break;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
value = effect =
graph()->NewNode(javascript()->CreateKeyValueArray(), value,
value, context, effect);
break;
case JS_MAP_VALUE_ITERATOR_TYPE:
value = effect = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForFixedArrayElement()),
table,
graph()->NewNode(
simplified()->NumberAdd(), entry_start_position,
jsgraph()->Constant(OrderedHashMap::kValueOffset)),
effect, control);
break;
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
value = effect = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForFixedArrayElement()),
table,
graph()->NewNode(
simplified()->NumberAdd(), entry_start_position,
jsgraph()->Constant(OrderedHashMap::kValueOffset)),
effect, control);
value = effect =
graph()->NewNode(javascript()->CreateKeyValueArray(),
entry_key, value, context, effect);
break;
default:
UNREACHABLE();
break;
}
// Store final {value} and {done} into the {iterator_result}.
effect =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForJSIteratorResultValue()),
iterator_result, value, effect, control);
effect =
graph()->NewNode(simplified()->StoreField(
AccessBuilder::ForJSIteratorResultDone()),
iterator_result, done, effect, control);
controls[1] = control;
effects[1] = effect;
}
// Continue with next loop index.
loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1));
eloop->ReplaceInput(1, etrue0);
iloop->ReplaceInput(1, index);
}
}
control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls);
effect = graph()->NewNode(common()->EffectPhi(2), 3, effects);
}
// Yield the final {iterator_result}.
ReplaceWithValue(node, iterator_result, effect, control);
return Replace(iterator_result);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
......@@ -158,6 +158,15 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceMapPrototypeHas(Node* node);
Reduction ReduceMapPrototypeGet(Node* node);
Reduction ReduceCollectionIteration(Node* node,
CollectionKind collection_kind,
IterationKind iteration_kind);
Reduction ReduceCollectionPrototypeSize(Node* node,
CollectionKind collection_kind);
Reduction ReduceCollectionIteratorPrototypeNext(
Node* node, int entry_size, Handle<HeapObject> empty_collection,
InstanceType collection_iterator_instance_type_first,
InstanceType collection_iterator_instance_type_last);
// Returns the updated {to} node, and updates control and effect along the
// way.
......
......@@ -143,6 +143,8 @@ Reduction JSCreateLowering::Reduce(Node* node) {
return ReduceJSCreateBoundFunction(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateCollectionIterator:
return ReduceJSCreateCollectionIterator(node);
case IrOpcode::kJSCreateIterResultObject:
return ReduceJSCreateIterResultObject(node);
case IrOpcode::kJSCreateStringIterator:
......@@ -911,6 +913,69 @@ Reduction JSCreateLowering::ReduceJSCreateArrayIterator(Node* node) {
return Changed(node);
}
namespace {
Context::Field ContextFieldForCollectionIterationKind(
CollectionKind collection_kind, IterationKind iteration_kind) {
switch (collection_kind) {
case CollectionKind::kSet:
switch (iteration_kind) {
case IterationKind::kKeys:
UNREACHABLE();
case IterationKind::kValues:
return Context::SET_VALUE_ITERATOR_MAP_INDEX;
case IterationKind::kEntries:
return Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX;
}
break;
case CollectionKind::kMap:
switch (iteration_kind) {
case IterationKind::kKeys:
return Context::MAP_KEY_ITERATOR_MAP_INDEX;
case IterationKind::kValues:
return Context::MAP_VALUE_ITERATOR_MAP_INDEX;
case IterationKind::kEntries:
return Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX;
}
break;
}
UNREACHABLE();
}
} // namespace
Reduction JSCreateLowering::ReduceJSCreateCollectionIterator(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateCollectionIterator, node->opcode());
CreateCollectionIteratorParameters const& p =
CreateCollectionIteratorParametersOf(node->op());
Node* iterated_object = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Load the OrderedHashTable from the {receiver}.
Node* table = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
iterated_object, effect, control);
// Create the JSArrayIterator result.
AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(JSCollectionIterator::kSize, NOT_TENURED, Type::OtherObject());
a.Store(AccessBuilder::ForMap(),
handle(native_context()->get(ContextFieldForCollectionIterationKind(
p.collection_kind(), p.iteration_kind())),
isolate()));
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSCollectionIteratorTable(), table);
a.Store(AccessBuilder::ForJSCollectionIteratorIndex(),
jsgraph()->ZeroConstant());
RelaxControls(node);
a.FinishAndChange(node);
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateBoundFunction(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateBoundFunction, node->opcode());
CreateBoundFunctionParameters const& p =
......
......@@ -51,6 +51,7 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateArrayIterator(Node* node);
Reduction ReduceJSCreateCollectionIterator(Node* node);
Reduction ReduceJSCreateBoundFunction(Node* node);
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
......
......@@ -381,6 +381,10 @@ void JSGenericLowering::LowerJSCreateArrayIterator(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
void JSGenericLowering::LowerJSCreateCollectionIterator(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
void JSGenericLowering::LowerJSCreateBoundFunction(Node* node) {
UNREACHABLE(); // Eliminated in typed lowering.
}
......
......@@ -442,6 +442,33 @@ const CreateArrayIteratorParameters& CreateArrayIteratorParametersOf(
return OpParameter<CreateArrayIteratorParameters>(op);
}
bool operator==(CreateCollectionIteratorParameters const& lhs,
CreateCollectionIteratorParameters const& rhs) {
return lhs.collection_kind() == rhs.collection_kind() &&
lhs.iteration_kind() == rhs.iteration_kind();
}
bool operator!=(CreateCollectionIteratorParameters const& lhs,
CreateCollectionIteratorParameters const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(CreateCollectionIteratorParameters const& p) {
return base::hash_combine(static_cast<size_t>(p.collection_kind()),
static_cast<size_t>(p.iteration_kind()));
}
std::ostream& operator<<(std::ostream& os,
CreateCollectionIteratorParameters const& p) {
return os << p.collection_kind() << " " << p.iteration_kind();
}
const CreateCollectionIteratorParameters& CreateCollectionIteratorParametersOf(
const Operator* op) {
DCHECK_EQ(IrOpcode::kJSCreateCollectionIterator, op->opcode());
return OpParameter<CreateCollectionIteratorParameters>(op);
}
bool operator==(CreateBoundFunctionParameters const& lhs,
CreateBoundFunctionParameters const& rhs) {
return lhs.arity() == rhs.arity() &&
......@@ -1105,6 +1132,15 @@ const Operator* JSOperatorBuilder::CreateArrayIterator(IterationKind kind) {
parameters); // parameter
}
const Operator* JSOperatorBuilder::CreateCollectionIterator(
CollectionKind collection_kind, IterationKind iteration_kind) {
CreateCollectionIteratorParameters parameters(collection_kind,
iteration_kind);
return new (zone()) Operator1<CreateCollectionIteratorParameters>(
IrOpcode::kJSCreateCollectionIterator, Operator::kEliminatable,
"JSCreateCollectionIterator", 1, 1, 1, 1, 1, 0, parameters);
}
const Operator* JSOperatorBuilder::CreateBoundFunction(size_t arity,
Handle<Map> map) {
// bound_target_function, bound_this, arg1, ..., argN
......
......@@ -526,6 +526,38 @@ std::ostream& operator<<(std::ostream&, CreateArrayIteratorParameters const&);
const CreateArrayIteratorParameters& CreateArrayIteratorParametersOf(
const Operator* op);
// Defines shared information for the array iterator that should be created.
// This is used as parameter by JSCreateCollectionIterator operators.
class CreateCollectionIteratorParameters final {
public:
explicit CreateCollectionIteratorParameters(CollectionKind collection_kind,
IterationKind iteration_kind)
: collection_kind_(collection_kind), iteration_kind_(iteration_kind) {
CHECK(!(collection_kind == CollectionKind::kSet &&
iteration_kind == IterationKind::kKeys));
}
CollectionKind collection_kind() const { return collection_kind_; }
IterationKind iteration_kind() const { return iteration_kind_; }
private:
CollectionKind const collection_kind_;
IterationKind const iteration_kind_;
};
bool operator==(CreateCollectionIteratorParameters const&,
CreateCollectionIteratorParameters const&);
bool operator!=(CreateCollectionIteratorParameters const&,
CreateCollectionIteratorParameters const&);
size_t hash_value(CreateCollectionIteratorParameters const&);
std::ostream& operator<<(std::ostream&,
CreateCollectionIteratorParameters const&);
const CreateCollectionIteratorParameters& CreateCollectionIteratorParametersOf(
const Operator* op);
// Defines shared information for the bound function that should be created.
// This is used as parameter by JSCreateBoundFunction operators.
class CreateBoundFunctionParameters final {
......@@ -686,6 +718,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateArguments(CreateArgumentsType type);
const Operator* CreateArray(size_t arity, Handle<AllocationSite> site);
const Operator* CreateArrayIterator(IterationKind);
const Operator* CreateCollectionIterator(CollectionKind, IterationKind);
const Operator* CreateBoundFunction(size_t arity, Handle<Map> map);
const Operator* CreateClosure(Handle<SharedFunctionInfo> shared_info,
Handle<FeedbackCell> feedback_cell,
......
......@@ -139,6 +139,7 @@
V(JSCreateArrayIterator) \
V(JSCreateBoundFunction) \
V(JSCreateClosure) \
V(JSCreateCollectionIterator) \
V(JSCreateGeneratorObject) \
V(JSCreateIterResultObject) \
V(JSCreateStringIterator) \
......
......@@ -1212,6 +1212,10 @@ Type* Typer::Visitor::TypeJSCreateArrayIterator(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateCollectionIterator(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateBoundFunction(Node* node) {
return Type::BoundFunction();
}
......
......@@ -669,6 +669,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateCollectionIterator:
// Type is OtherObject.
CheckTypeIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateBoundFunction:
// Type is BoundFunction.
CheckTypeIs(node, Type::BoundFunction());
......
......@@ -1370,6 +1370,18 @@ inline std::ostream& operator<<(std::ostream& os, IterationKind kind) {
UNREACHABLE();
}
enum class CollectionKind { kMap, kSet };
inline std::ostream& operator<<(std::ostream& os, CollectionKind kind) {
switch (kind) {
case CollectionKind::kMap:
return os << "CollectionKind::kMap";
case CollectionKind::kSet:
return os << "CollectionKind::kSet";
}
UNREACHABLE();
}
// Flags for the runtime function kDefineDataPropertyInLiteral. A property can
// be enumerable or not, and, in case of functions, the function name
// can be set or not.
......
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