Skip to content

Commit

Permalink
[turbofan] Reduce ArrayIteratorNext based on instance type
Browse files Browse the repository at this point in the history
NodeProperties::InferReceiverMaps now traverses effect chain for
Loop-EffectPhi nodes, which makes it possible to inline `iterator.next()`
within a loop when the next property is loaded outside of a loop.

A new helper, GetInstanceTypeWitness(), performs InferReceiverMaps() and
checks that each resulting map has an identical instance type.

BUG=chromium:795632, v8:5940, v8:3018
R=bmeurer@chromium.org, jarin@chromium.org

Change-Id: Id2690c224668bea62dbcad62ebc2bdf7e37e80d3
Reviewed-on: https://chromium-review.googlesource.com/837484
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50284}
  • Loading branch information
caitp authored and Commit Bot committed Dec 21, 2017
1 parent 7bcd926 commit dcd60e8
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 68 deletions.
145 changes: 79 additions & 66 deletions src/compiler/js-builtin-reducer.cc
Expand Up @@ -121,6 +121,24 @@ MaybeHandle<Map> GetMapWitness(Node* node) {
return MaybeHandle<Map>();
}

Maybe<InstanceType> GetInstanceTypeWitness(Node* node) {
ZoneHandleSet<Map> maps;
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &maps);

if (result == NodeProperties::kNoReceiverMaps || maps.size() == 0) {
return Nothing<InstanceType>();
}

InstanceType first_type = maps[0]->instance_type();
for (const Handle<Map>& map : maps) {
if (map->instance_type() != first_type) return Nothing<InstanceType>();
}
return Just(first_type);
}

// TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
bool IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) {
DCHECK(!jsarray_map->is_dictionary_map());
Expand Down Expand Up @@ -313,8 +331,9 @@ Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map,
return Replace(value);
}

Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
Handle<Map> iterator_map, Node* node, IterationKind kind) {
Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(InstanceType type,
Node* node,
IterationKind kind) {
Node* iterator = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Expand All @@ -327,8 +346,8 @@ Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
return NoChange();
}

ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType(
iterator_map->instance_type());
ElementsKind elements_kind =
JSArrayIterator::ElementsKindForInstanceType(type);

if (IsHoleyElementsKind(elements_kind)) {
if (!isolate()->IsNoElementsProtectorIntact()) {
Expand Down Expand Up @@ -484,15 +503,16 @@ Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
return Replace(value);
}

Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(
Handle<Map> iterator_map, Node* node, IterationKind kind) {
Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(InstanceType type,
Node* node,
IterationKind kind) {
Node* iterator = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);

ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType(
iterator_map->instance_type());
ElementsKind elements_kind =
JSArrayIterator::ElementsKindForInstanceType(type);

Node* array = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()),
Expand Down Expand Up @@ -725,65 +745,58 @@ Reduction JSBuiltinReducer::ReduceTypedArrayToStringTag(Node* node) {
}

Reduction JSBuiltinReducer::ReduceArrayIteratorNext(Node* node) {
Handle<Map> receiver_map;
if (GetMapWitness(node).ToHandle(&receiver_map)) {
switch (receiver_map->instance_type()) {
case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(receiver_map, node,
IterationKind::kKeys);

case JS_FAST_ARRAY_KEY_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(receiver_map, node,
IterationKind::kKeys);

case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(receiver_map, node,
IterationKind::kEntries);

case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(receiver_map, node,
IterationKind::kEntries);

case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(receiver_map, node,
IterationKind::kValues);

case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(receiver_map, node,
IterationKind::kValues);

default:
// Slow array iterators are not reduced
return NoChange();
}
Maybe<InstanceType> maybe_type = GetInstanceTypeWitness(node);
if (!maybe_type.IsJust()) return NoChange();
InstanceType type = maybe_type.FromJust();
switch (type) {
case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(type, node, IterationKind::kKeys);

case JS_FAST_ARRAY_KEY_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(type, node, IterationKind::kKeys);

case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(type, node, IterationKind::kEntries);

case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(type, node, IterationKind::kEntries);

case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE:
return ReduceTypedArrayIteratorNext(type, node, IterationKind::kValues);

case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
return ReduceFastArrayIteratorNext(type, node, IterationKind::kValues);

default:
// Slow array iterators are not reduced
return NoChange();
}
return NoChange();
}

// ES6 section 22.1.2.2 Array.isArray ( arg )
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/js-builtin-reducer.h
Expand Up @@ -47,9 +47,9 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
IterationKind kind,
ArrayIteratorKind iter_kind);
Reduction ReduceArrayIteratorNext(Node* node);
Reduction ReduceFastArrayIteratorNext(Handle<Map> iterator_map, Node* node,
Reduction ReduceFastArrayIteratorNext(InstanceType type, Node* node,
IterationKind kind);
Reduction ReduceTypedArrayIteratorNext(Handle<Map> iterator_map, Node* node,
Reduction ReduceTypedArrayIteratorNext(InstanceType type, Node* node,
IterationKind kind);
Reduction ReduceTypedArrayToStringTag(Node* node);
Reduction ReduceArrayIsArray(Node* node);
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/node-properties.cc
Expand Up @@ -461,6 +461,19 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMaps(
if (IsSame(receiver, effect)) receiver = GetValueInput(effect, 0);
break;
}
case IrOpcode::kEffectPhi: {
Node* control = GetControlInput(effect);
if (control->opcode() != IrOpcode::kLoop) {
DCHECK_EQ(IrOpcode::kMerge, control->opcode());
return kNoReceiverMaps;
}

// Continue search for receiver map outside the loop. Since operations
// inside the loop may change the map, the result is unreliable.
effect = GetEffectInput(effect, 0);
result = kUnreliableReceiverMaps;
continue;
}
default: {
DCHECK_EQ(1, effect->op()->EffectOutputCount());
if (effect->op()->EffectInputCount() != 1) {
Expand Down

0 comments on commit dcd60e8

Please sign in to comment.