| Index: src/compiler/js-builtin-reducer.cc | 
| diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc | 
| index 8bc03fae6239b850a901aab90cc41bf09ee30983..50d40c0c043adfcee48649fab63b62dcf8fec552 100644 | 
| --- a/src/compiler/js-builtin-reducer.cc | 
| +++ b/src/compiler/js-builtin-reducer.cc | 
| @@ -175,8 +175,551 @@ bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) { | 
| !IsReadOnlyLengthDescriptor(receiver_map); | 
| } | 
|  | 
| +bool CanInlineJSArrayIteration(Handle<Map> receiver_map) { | 
| +  Isolate* const isolate = receiver_map->GetIsolate(); | 
| +  // Ensure that the [[Prototype]] is actually an exotic Array | 
| +  if (!receiver_map->prototype()->IsJSArray()) return false; | 
| + | 
| +  // Don't inline JSArrays with slow elements of any kind | 
| +  if (!IsFastElementsKind(receiver_map->elements_kind())) return false; | 
| + | 
| +  // If the receiver map has packed elements, no need to check the prototype. | 
| +  // This requires a MapCheck where this is used. | 
| +  if (!IsFastHoleyElementsKind(receiver_map->elements_kind())) return true; | 
| + | 
| +  Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()), | 
| +                                     isolate); | 
| +  // Ensure all prototypes of the {receiver} are stable. | 
| +  for (PrototypeIterator it(isolate, receiver_prototype, kStartAtReceiver); | 
| +       !it.IsAtEnd(); it.Advance()) { | 
| +    Handle<JSReceiver> current = PrototypeIterator::GetCurrent<JSReceiver>(it); | 
| +    if (!current->map()->is_stable()) return false; | 
| +  } | 
| + | 
| +  // For holey Arrays, ensure that the array_protector cell is valid (must be | 
| +  // a CompilationDependency), and the JSArray prototype has not been altered. | 
| +  return receiver_map->instance_type() == JS_ARRAY_TYPE && | 
| +         (!receiver_map->is_dictionary_map() || receiver_map->is_stable()) && | 
| +         isolate->IsFastArrayConstructorPrototypeChainIntact() && | 
| +         isolate->IsAnyInitialArrayPrototype(receiver_prototype); | 
| +} | 
| + | 
| }  // namespace | 
|  | 
| +Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node, | 
| +                                                IterationKind kind) { | 
| +  Handle<Map> receiver_map; | 
| +  if (GetMapWitness(node).ToHandle(&receiver_map)) { | 
| +    return ReduceArrayIterator(receiver_map, node, kind, | 
| +                               ArrayIteratorKind::kArray); | 
| +  } | 
| +  return NoChange(); | 
| +} | 
| + | 
| +Reduction JSBuiltinReducer::ReduceTypedArrayIterator(Node* node, | 
| +                                                     IterationKind kind) { | 
| +  Handle<Map> receiver_map; | 
| +  if (GetMapWitness(node).ToHandle(&receiver_map) && | 
| +      receiver_map->instance_type() == JS_TYPED_ARRAY_TYPE) { | 
| +    return ReduceArrayIterator(receiver_map, node, kind, | 
| +                               ArrayIteratorKind::kTypedArray); | 
| +  } | 
| +  return NoChange(); | 
| +} | 
| + | 
| +Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map, | 
| +                                                Node* node, IterationKind kind, | 
| +                                                ArrayIteratorKind iter_kind) { | 
| +  Node* receiver = NodeProperties::GetValueInput(node, 1); | 
| +  Node* effect = NodeProperties::GetEffectInput(node); | 
| +  Node* control = NodeProperties::GetControlInput(node); | 
| + | 
| +  if (iter_kind == ArrayIteratorKind::kTypedArray) { | 
| +    // For JSTypedArray iterator methods, deopt if the buffer is neutered. This | 
| +    // is potentially a deopt loop, but should be extremely unlikely. | 
| +    DCHECK_EQ(JS_TYPED_ARRAY_TYPE, receiver_map->instance_type()); | 
| +    Node* buffer = effect = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), | 
| +        receiver, effect, control); | 
| + | 
| +    Node* check = effect = graph()->NewNode( | 
| +        simplified()->ArrayBufferWasNeutered(), buffer, effect, control); | 
| +    check = graph()->NewNode(simplified()->BooleanNot(), check); | 
| +    effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); | 
| +  } | 
| + | 
| +  int map_index = -1; | 
| +  Node* object_map = jsgraph()->UndefinedConstant(); | 
| +  switch (receiver_map->instance_type()) { | 
| +    case JS_ARRAY_TYPE: | 
| +      if (kind == IterationKind::kKeys) { | 
| +        map_index = Context::FAST_ARRAY_KEY_ITERATOR_MAP_INDEX; | 
| +      } else { | 
| +        map_index = kind == IterationKind::kValues | 
| +                        ? Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX | 
| +                        : Context::FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX; | 
| + | 
| +        if (CanInlineJSArrayIteration(receiver_map)) { | 
| +          // Use `generic` elements for holey arrays if there may be elements | 
| +          // on the prototype chain. | 
| +          map_index += static_cast<int>(receiver_map->elements_kind()); | 
| +          object_map = jsgraph()->Constant(receiver_map); | 
| +          if (IsFastHoleyElementsKind(receiver_map->elements_kind())) { | 
| +            Handle<JSObject> initial_array_prototype( | 
| +                native_context()->initial_array_prototype(), isolate()); | 
| +            dependencies()->AssumePrototypeMapsStable(receiver_map, | 
| +                                                      initial_array_prototype); | 
| +          } | 
| +        } else { | 
| +          map_index += (Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX - | 
| +                        Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX); | 
| +        } | 
| +      } | 
| +      break; | 
| +    case JS_TYPED_ARRAY_TYPE: | 
| +      if (kind == IterationKind::kKeys) { | 
| +        map_index = Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX; | 
| +      } else { | 
| +        DCHECK_GE(receiver_map->elements_kind(), UINT8_ELEMENTS); | 
| +        DCHECK_LE(receiver_map->elements_kind(), UINT8_CLAMPED_ELEMENTS); | 
| +        map_index = (kind == IterationKind::kValues | 
| +                         ? Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX | 
| +                         : Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX) + | 
| +                    (receiver_map->elements_kind() - UINT8_ELEMENTS); | 
| +      } | 
| +      break; | 
| +    default: | 
| +      if (kind == IterationKind::kKeys) { | 
| +        map_index = Context::GENERIC_ARRAY_KEY_ITERATOR_MAP_INDEX; | 
| +      } else if (kind == IterationKind::kValues) { | 
| +        map_index = Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX; | 
| +      } else { | 
| +        map_index = Context::GENERIC_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX; | 
| +      } | 
| +      break; | 
| +  } | 
| + | 
| +  DCHECK_GE(map_index, Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX); | 
| +  DCHECK_LE(map_index, Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX); | 
| + | 
| +  Handle<Map> map(Map::cast(native_context()->get(map_index)), isolate()); | 
| + | 
| +  // allocate new iterator | 
| +  effect = graph()->NewNode( | 
| +      common()->BeginRegion(RegionObservability::kNotObservable), effect); | 
| +  Node* value = effect = graph()->NewNode( | 
| +      simplified()->Allocate(NOT_TENURED), | 
| +      jsgraph()->Constant(JSArrayIterator::kSize), effect, control); | 
| +  effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()), | 
| +                            value, jsgraph()->Constant(map), effect, control); | 
| +  effect = graph()->NewNode( | 
| +      simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value, | 
| +      jsgraph()->EmptyFixedArrayConstant(), effect, control); | 
| +  effect = graph()->NewNode( | 
| +      simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value, | 
| +      jsgraph()->EmptyFixedArrayConstant(), effect, control); | 
| + | 
| +  // attach the iterator to this object | 
| +  effect = graph()->NewNode( | 
| +      simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()), | 
| +      value, receiver, effect, control); | 
| +  effect = graph()->NewNode( | 
| +      simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex()), value, | 
| +      jsgraph()->ZeroConstant(), effect, control); | 
| +  effect = graph()->NewNode( | 
| +      simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObjectMap()), | 
| +      value, object_map, effect, control); | 
| + | 
| +  value = effect = graph()->NewNode(common()->FinishRegion(), value, effect); | 
| + | 
| +  // replace it | 
| +  ReplaceWithValue(node, value, effect, control); | 
| +  return Replace(value); | 
| +} | 
| + | 
| +Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext( | 
| +    Handle<Map> iterator_map, 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); | 
| + | 
| +  if (kind != IterationKind::kKeys && !isolate()->CanInlineArrayIterator()) { | 
| +    // Avoid deopt loops for non-key iteration if the array_iterator_protector | 
| +    // cell has been invalidated. | 
| +    return NoChange(); | 
| +  } | 
| + | 
| +  ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType( | 
| +      iterator_map->instance_type()); | 
| + | 
| +  if (IsFastHoleyElementsKind(elements_kind)) { | 
| +    if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) { | 
| +      return NoChange(); | 
| +    } else { | 
| +      Handle<JSObject> initial_array_prototype( | 
| +          native_context()->initial_array_prototype(), isolate()); | 
| +      dependencies()->AssumePropertyCell(factory()->array_protector()); | 
| +    } | 
| +  } | 
| + | 
| +  Node* array = effect = graph()->NewNode( | 
| +      simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()), | 
| +      iterator, effect, control); | 
| +  Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array, | 
| +                                  jsgraph()->UndefinedConstant()); | 
| +  Node* branch0 = | 
| +      graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); | 
| + | 
| +  Node* vdone_false0; | 
| +  Node* vfalse0; | 
| +  Node* efalse0 = effect; | 
| +  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
| +  { | 
| +    // iterator.[[IteratedObject]] !== undefined, continue iterating. | 
| +    Node* index = efalse0 = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex( | 
| +            JS_ARRAY_TYPE, elements_kind)), | 
| +        iterator, efalse0, if_false0); | 
| + | 
| +    Node* length = efalse0 = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)), | 
| +        array, efalse0, if_false0); | 
| +    Node* check1 = | 
| +        graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
| +    Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 
| +                                     check1, if_false0); | 
| + | 
| +    Node* vdone_true1; | 
| +    Node* vtrue1; | 
| +    Node* etrue1 = efalse0; | 
| +    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); | 
| +    { | 
| +      // iterator.[[NextIndex]] < array.length, continue iterating | 
| +      vdone_true1 = jsgraph()->FalseConstant(); | 
| +      if (kind == IterationKind::kKeys) { | 
| +        vtrue1 = index; | 
| +      } else { | 
| +        // For value/entry iteration, first step is a mapcheck to ensure | 
| +        // inlining is still valid. | 
| +        Node* orig_map = etrue1 = | 
| +            graph()->NewNode(simplified()->LoadField( | 
| +                                 AccessBuilder::ForJSArrayIteratorObjectMap()), | 
| +                             iterator, etrue1, if_true1); | 
| +        etrue1 = graph()->NewNode(simplified()->CheckMaps(1), array, orig_map, | 
| +                                  etrue1, if_true1); | 
| +      } | 
| + | 
| +      if (kind != IterationKind::kKeys) { | 
| +        Node* elements = etrue1 = graph()->NewNode( | 
| +            simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | 
| +            array, etrue1, if_true1); | 
| +        Node* value = etrue1 = graph()->NewNode( | 
| +            simplified()->LoadElement( | 
| +                AccessBuilder::ForFixedArrayElement(elements_kind)), | 
| +            elements, index, etrue1, if_true1); | 
| + | 
| +        // Convert hole to undefined if needed. | 
| +        if (elements_kind == FAST_HOLEY_ELEMENTS || | 
| +            elements_kind == FAST_HOLEY_SMI_ELEMENTS) { | 
| +          value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), | 
| +                                   value); | 
| +        } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { | 
| +          // TODO(bmeurer): avoid deopt if not all uses of value are truncated. | 
| +          CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole; | 
| +          value = etrue1 = graph()->NewNode( | 
| +              simplified()->CheckFloat64Hole(mode), value, etrue1, if_true1); | 
| +        } | 
| + | 
| +        if (kind == IterationKind::kEntries) { | 
| +          // Allocate elements for key/value pair | 
| +          vtrue1 = etrue1 = graph()->NewNode( | 
| +              javascript()->CreateKeyValueArray(), index, value, etrue1); | 
| +        } else { | 
| +          DCHECK_EQ(kind, IterationKind::kValues); | 
| +          vtrue1 = value; | 
| +        } | 
| +      } | 
| + | 
| +      Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, | 
| +                                          jsgraph()->OneConstant()); | 
| +      next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index); | 
| + | 
| +      etrue1 = graph()->NewNode( | 
| +          simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex( | 
| +              JS_ARRAY_TYPE, elements_kind)), | 
| +          iterator, next_index, etrue1, if_true1); | 
| +    } | 
| + | 
| +    Node* vdone_false1; | 
| +    Node* vfalse1; | 
| +    Node* efalse1 = efalse0; | 
| +    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); | 
| +    { | 
| +      // iterator.[[NextIndex]] >= array.length, stop iterating. | 
| +      vdone_false1 = jsgraph()->TrueConstant(); | 
| +      vfalse1 = jsgraph()->UndefinedConstant(); | 
| +      efalse1 = graph()->NewNode( | 
| +          simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()), | 
| +          iterator, vfalse1, efalse1, if_false1); | 
| +    } | 
| + | 
| +    if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); | 
| +    efalse0 = | 
| +        graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); | 
| +    vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                               vtrue1, vfalse1, if_false0); | 
| +    vdone_false0 = | 
| +        graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                         vdone_true1, vdone_false1, if_false0); | 
| +  } | 
| + | 
| +  Node* vdone_true0; | 
| +  Node* vtrue0; | 
| +  Node* etrue0 = effect; | 
| +  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
| +  { | 
| +    // iterator.[[IteratedObject]] === undefined, the iterator is done. | 
| +    vdone_true0 = jsgraph()->TrueConstant(); | 
| +    vtrue0 = jsgraph()->UndefinedConstant(); | 
| +  } | 
| + | 
| +  control = graph()->NewNode(common()->Merge(2), if_false0, if_true0); | 
| +  effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control); | 
| +  Node* value = | 
| +      graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                       vfalse0, vtrue0, control); | 
| +  Node* done = | 
| +      graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                       vdone_false0, vdone_true0, control); | 
| + | 
| +  // Create IteratorResult object. | 
| +  value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), | 
| +                                    value, done, context, effect); | 
| +  ReplaceWithValue(node, value, effect, control); | 
| +  return Replace(value); | 
| +} | 
| + | 
| +Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext( | 
| +    Handle<Map> iterator_map, 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()); | 
| + | 
| +  Node* array = effect = graph()->NewNode( | 
| +      simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()), | 
| +      iterator, effect, control); | 
| +  Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array, | 
| +                                  jsgraph()->UndefinedConstant()); | 
| +  Node* branch0 = | 
| +      graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); | 
| + | 
| +  Node* vdone_false0; | 
| +  Node* vfalse0; | 
| +  Node* efalse0 = effect; | 
| +  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); | 
| +  { | 
| +    // iterator.[[IteratedObject]] !== undefined, continue iterating. | 
| +    Node* index = efalse0 = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex( | 
| +            JS_TYPED_ARRAY_TYPE, elements_kind)), | 
| +        iterator, efalse0, if_false0); | 
| + | 
| +    // typedarray.[[ViewedArrayBuffer]] | 
| +    Node* buffer = efalse0 = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), | 
| +        array, efalse0, if_false0); | 
| + | 
| +    Node* check1 = efalse0 = graph()->NewNode( | 
| +        simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0); | 
| +    check1 = graph()->NewNode(simplified()->BooleanNot(), check1); | 
| +    efalse0 = | 
| +        graph()->NewNode(simplified()->CheckIf(), check1, efalse0, if_false0); | 
| + | 
| +    Node* length = efalse0 = graph()->NewNode( | 
| +        simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), array, | 
| +        efalse0, if_false0); | 
| + | 
| +    Node* check2 = | 
| +        graph()->NewNode(simplified()->NumberLessThan(), index, length); | 
| +    Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 
| +                                     check2, if_false0); | 
| + | 
| +    Node* vdone_true2; | 
| +    Node* vtrue2; | 
| +    Node* etrue2 = efalse0; | 
| +    Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); | 
| +    { | 
| +      // iterator.[[NextIndex]] < array.length, continue iterating | 
| +      vdone_true2 = jsgraph()->FalseConstant(); | 
| +      if (kind == IterationKind::kKeys) { | 
| +        vtrue2 = index; | 
| +      } | 
| + | 
| +      Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, | 
| +                                          jsgraph()->OneConstant()); | 
| +      next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index); | 
| + | 
| +      etrue2 = graph()->NewNode( | 
| +          simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex( | 
| +              JS_TYPED_ARRAY_TYPE, elements_kind)), | 
| +          iterator, next_index, etrue2, if_true2); | 
| + | 
| +      if (kind != IterationKind::kKeys) { | 
| +        Node* elements = etrue2 = graph()->NewNode( | 
| +            simplified()->LoadField(AccessBuilder::ForJSObjectElements()), | 
| +            array, etrue2, if_true2); | 
| +        Node* base_ptr = etrue2 = graph()->NewNode( | 
| +            simplified()->LoadField( | 
| +                AccessBuilder::ForFixedTypedArrayBaseBasePointer()), | 
| +            elements, etrue2, if_true2); | 
| +        Node* external_ptr = etrue2 = graph()->NewNode( | 
| +            simplified()->LoadField( | 
| +                AccessBuilder::ForFixedTypedArrayBaseExternalPointer()), | 
| +            elements, etrue2, if_true2); | 
| + | 
| +        ExternalArrayType array_type = kExternalInt8Array; | 
| +        switch (elements_kind) { | 
| +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ | 
| +  case TYPE##_ELEMENTS:                                 \ | 
| +    array_type = kExternal##Type##Array;                \ | 
| +    break; | 
| +          TYPED_ARRAYS(TYPED_ARRAY_CASE) | 
| +          default: | 
| +            UNREACHABLE(); | 
| +#undef TYPED_ARRAY_CASE | 
| +        } | 
| + | 
| +        Node* value = etrue2 = | 
| +            graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer, | 
| +                             base_ptr, external_ptr, index, etrue2, if_true2); | 
| + | 
| +        if (kind == IterationKind::kEntries) { | 
| +          // Allocate elements for key/value pair | 
| +          vtrue2 = etrue2 = graph()->NewNode( | 
| +              javascript()->CreateKeyValueArray(), index, value, etrue2); | 
| +        } else { | 
| +          DCHECK(kind == IterationKind::kValues); | 
| +          vtrue2 = value; | 
| +        } | 
| +      } | 
| +    } | 
| + | 
| +    Node* vdone_false2; | 
| +    Node* vfalse2; | 
| +    Node* efalse2 = efalse0; | 
| +    Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); | 
| +    { | 
| +      // iterator.[[NextIndex]] >= array.length, stop iterating. | 
| +      vdone_false2 = jsgraph()->TrueConstant(); | 
| +      vfalse2 = jsgraph()->UndefinedConstant(); | 
| +      efalse2 = graph()->NewNode( | 
| +          simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()), | 
| +          iterator, vfalse2, efalse2, if_false2); | 
| +    } | 
| + | 
| +    if_false0 = graph()->NewNode(common()->Merge(2), if_true2, if_false2); | 
| +    efalse0 = | 
| +        graph()->NewNode(common()->EffectPhi(2), etrue2, efalse2, if_false0); | 
| +    vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                               vtrue2, vfalse2, if_false0); | 
| +    vdone_false0 = | 
| +        graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                         vdone_true2, vdone_false2, if_false0); | 
| +  } | 
| + | 
| +  Node* vdone_true0; | 
| +  Node* vtrue0; | 
| +  Node* etrue0 = effect; | 
| +  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); | 
| +  { | 
| +    // iterator.[[IteratedObject]] === undefined, the iterator is done. | 
| +    vdone_true0 = jsgraph()->TrueConstant(); | 
| +    vtrue0 = jsgraph()->UndefinedConstant(); | 
| +  } | 
| + | 
| +  control = graph()->NewNode(common()->Merge(2), if_false0, if_true0); | 
| +  effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control); | 
| +  Node* value = | 
| +      graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                       vfalse0, vtrue0, control); | 
| +  Node* done = | 
| +      graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), | 
| +                       vdone_false0, vdone_true0, control); | 
| + | 
| +  // Create IteratorResult object. | 
| +  value = effect = graph()->NewNode(javascript()->CreateIterResultObject(), | 
| +                                    value, done, context, effect); | 
| +  ReplaceWithValue(node, value, effect, control); | 
| +  return Replace(value); | 
| +} | 
| + | 
| +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(); | 
| +    } | 
| +  } | 
| +  return NoChange(); | 
| +} | 
| + | 
| // ES6 section 22.1.3.17 Array.prototype.pop ( ) | 
| Reduction JSBuiltinReducer::ReduceArrayPop(Node* node) { | 
| Handle<Map> receiver_map; | 
| @@ -1260,6 +1803,14 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { | 
| // Dispatch according to the BuiltinFunctionId if present. | 
| if (!r.HasBuiltinFunctionId()) return NoChange(); | 
| switch (r.GetBuiltinFunctionId()) { | 
| +    case kArrayEntries: | 
| +      return ReduceArrayIterator(node, IterationKind::kEntries); | 
| +    case kArrayKeys: | 
| +      return ReduceArrayIterator(node, IterationKind::kKeys); | 
| +    case kArrayValues: | 
| +      return ReduceArrayIterator(node, IterationKind::kValues); | 
| +    case kArrayIteratorNext: | 
| +      return ReduceArrayIteratorNext(node); | 
| case kArrayPop: | 
| return ReduceArrayPop(node); | 
| case kArrayPush: | 
| @@ -1416,6 +1967,12 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { | 
| case kTypedArrayLength: | 
| return ReduceArrayBufferViewAccessor( | 
| node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength()); | 
| +    case kTypedArrayEntries: | 
| +      return ReduceTypedArrayIterator(node, IterationKind::kEntries); | 
| +    case kTypedArrayKeys: | 
| +      return ReduceTypedArrayIterator(node, IterationKind::kKeys); | 
| +    case kTypedArrayValues: | 
| +      return ReduceTypedArrayIterator(node, IterationKind::kValues); | 
| default: | 
| break; | 
| } | 
|  |